Skip to content

Commit b7abb41

Browse files
committed
新增 xxljob3-solon-cloud-plugin 插件
1 parent 168ee82 commit b7abb41

File tree

13 files changed

+537
-0
lines changed

13 files changed

+537
-0
lines changed

UPDATE_LOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11

22
### v3.7.3
33

4+
* 新增 xxljob3-solon-cloud-plugin 插件
45
* 修复 solon-server-jetty-jakarta jsp 支持
56
* 添加 solon-server-tomcat-jakarta jsp 支持
7+
* 添加 solon-server-tomcat-jakarta websocket 支持
68

79
### v3.7.0
810

__release/solon-jakarta-bundle1/pom.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,8 @@
4141

4242
<module>../../solon-jakarta-projects/solon-web/solon-web-servlet-jakarta</module>
4343
<module>../../solon-jakarta-projects/solon-web/solon-web-webservices-jakarta</module>
44+
45+
46+
<module>../../solon-jakarta-projects/solon-cloud/xxljob3-solon-cloud-plugin</module>
4447
</modules>
4548
</project>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
#### 配置示例
3+
4+
```yml
5+
solon.app:
6+
group: demo #配置服务使用的默认组
7+
name: helloapp #发现服务使用的应用名
8+
9+
solon.cloud.xxljob:
10+
server: "http://localhost:8093/xxl-job-admin"
11+
12+
13+
### xxl-job admin address list, (默认:${solon.cloud.xxljob.server})
14+
#xxl.job.admin.addresses: "${solon.cloud.xxljob.server}"
15+
### xxl-job access token (默认:${solon.cloud.xxljob.token})
16+
#xxl.job.accessToken:
17+
### xxl-job executor appname(默认:${solon.app.name})
18+
#xxl.job.executor.appname:
19+
### xxl-job executor registry-address: 默认使用 address 注册 , 没有配置则用 ip:port(建议用 ip:port)
20+
#xxl.job.executor.address:
21+
### xxl-job executor server-info(默认:本机内网ip)
22+
#xxl.job.executor.ip:
23+
### xxl-job executor server-info(默认:9999)
24+
#xxl.job.executor.port: 9999
25+
### xxl-job executor log-path(默认:/data/logs/xxl-job/jobhandler)
26+
#xxl.job.executor.logpath:
27+
### xxl-job executor log-retention-days(默认:30)
28+
#xxl.job.executor.logretentiondays: 30
29+
```
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.noear</groupId>
9+
<artifactId>solon-jakarta-parent</artifactId>
10+
<version>3.7.3-SNAPSHOT</version>
11+
<relativePath>../../../solon-jakarta-parent/pom.xml</relativePath>
12+
</parent>
13+
14+
<artifactId>xxljob3-solon-cloud-plugin</artifactId>
15+
<name>${project.artifactId}</name>
16+
<packaging>jar</packaging>
17+
18+
<dependencies>
19+
<dependency>
20+
<groupId>org.noear</groupId>
21+
<artifactId>solon-cloud</artifactId>
22+
</dependency>
23+
24+
<dependency>
25+
<groupId>com.xuxueli</groupId>
26+
<artifactId>xxl-job-core</artifactId>
27+
<version>3.3.0</version>
28+
</dependency>
29+
</dependencies>
30+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2017-2025 noear.org and authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.noear.solon.cloud.extend.xxljob3.integration;
17+
18+
import com.xxl.job.core.executor.XxlJobExecutor;
19+
import org.noear.solon.Solon;
20+
import org.noear.solon.Utils;
21+
import org.noear.solon.annotation.Bean;
22+
import org.noear.solon.annotation.Configuration;
23+
import org.noear.solon.annotation.Inject;
24+
import org.noear.solon.cloud.CloudProps;
25+
import org.noear.solon.cloud.utils.LocalUtils;
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
29+
/**
30+
* auto config XxlJobExecutor
31+
*
32+
* @author noear
33+
* @since 1.4
34+
*/
35+
@Configuration
36+
public class XxlJob3AutoConfig {
37+
private static final Logger logger = LoggerFactory.getLogger(XxlJob3AutoConfig.class);
38+
39+
@Inject(value = "${xxl.job.admin.addresses}", required = false)
40+
private String adminAddresses;
41+
42+
@Inject(value = "${xxl.job.accessToken}", required = false)
43+
private String accessToken;
44+
45+
@Inject(value = "${xxl.job.executor.appname}", required = false)
46+
private String appname;
47+
48+
@Inject(value = "${xxl.job.executor.address}", required = false)
49+
private String address;
50+
51+
@Inject(value = "${xxl.job.executor.ip}", required = false)
52+
private String ip;
53+
54+
@Inject(value = "${xxl.job.executor.port}", required = false)
55+
private int port;
56+
57+
@Inject(value = "${xxl.job.executor.logpath}", required = false)
58+
private String logPath;
59+
60+
@Inject(value = "${xxl.job.executor.logretentiondays}", required = false)
61+
private int logRetentionDays;
62+
63+
@Inject("xxljob-cloudProps")
64+
CloudProps cloudProps;
65+
66+
@Bean
67+
public XxlJobExecutor xxlJobExecutor() {
68+
logger.info(">>>>>>>>>>> xxl-job config init.");
69+
70+
if (Utils.isEmpty(adminAddresses)) {
71+
adminAddresses = cloudProps.getJobServer();
72+
}
73+
74+
if (Utils.isEmpty(appname)) {
75+
appname = Solon.cfg().appName();
76+
}
77+
78+
if (Utils.isEmpty(ip)) {
79+
ip = LocalUtils.getLocalAddress();
80+
}
81+
82+
if (port < 1000) {
83+
port = 9999;
84+
}
85+
86+
if (logRetentionDays < 1) {
87+
logRetentionDays = 30;
88+
}
89+
90+
if (Utils.isEmpty(logPath)) {
91+
logPath = "/data/applogs/xxl-job/jobhandler";
92+
}
93+
94+
if (Utils.isEmpty(accessToken)) {
95+
accessToken = cloudProps.getToken();
96+
if (Utils.isEmpty(accessToken)) {
97+
//兼容旧的
98+
accessToken = cloudProps.getPassword();
99+
}
100+
}
101+
102+
103+
XxlJobExecutor executor = new XxlJobExecutor();
104+
105+
executor.setAdminAddresses(adminAddresses);
106+
executor.setAppname(appname);
107+
executor.setAddress(address);
108+
executor.setIp(ip);
109+
executor.setPort(port);
110+
executor.setAccessToken(accessToken);
111+
executor.setLogPath(logPath);
112+
executor.setLogRetentionDays(logRetentionDays);
113+
114+
return executor;
115+
}
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2017-2025 noear.org and authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.noear.solon.cloud.extend.xxljob3.integration;
17+
18+
import com.xxl.job.core.executor.XxlJobExecutor;
19+
import com.xxl.job.core.handler.IJobHandler;
20+
import com.xxl.job.core.handler.annotation.XxlJob;
21+
import org.noear.solon.Solon;
22+
import org.noear.solon.Utils;
23+
import org.noear.solon.cloud.CloudManager;
24+
import org.noear.solon.cloud.CloudProps;
25+
import org.noear.solon.cloud.annotation.CloudJob;
26+
import org.noear.solon.cloud.extend.xxljob3.service.CloudJobServiceImpl;
27+
import org.noear.solon.core.AppContext;
28+
import org.noear.solon.core.BeanWrap;
29+
import org.noear.solon.core.Plugin;
30+
import org.noear.solon.core.event.AppLoadEndEvent;
31+
32+
/**
33+
* @author noear
34+
* @since 1.4
35+
*/
36+
public class XxlJob3CloudPlugin implements Plugin {
37+
@Override
38+
public void start(AppContext context) {
39+
CloudProps cloudProps = new CloudProps(context, "xxljob");
40+
41+
if (Utils.isEmpty(cloudProps.getServer())) {
42+
return;
43+
}
44+
45+
if (!cloudProps.getJobEnable()) {
46+
return;
47+
}
48+
49+
//注册 bean 给 XxlJobAutoConfig 用
50+
BeanWrap beanWrap = context.wrap("xxljob-cloudProps", cloudProps);
51+
context.putWrap("xxljob-cloudProps", beanWrap);
52+
53+
///////////////////////////////////////////////////
54+
55+
//注册Job服务
56+
CloudManager.register(new CloudJobServiceImpl());
57+
58+
//添加 @CloudJob 对 IJobHandler 类的支持 //@since 2.0 //@since 2.9
59+
context.beanBuilderAdd(CloudJob.class, IJobHandler.class, (clz, bw, anno) -> {
60+
//支持${xxx}配置
61+
String name = Solon.cfg().getByTmpl(Utils.annoAlias(anno.value(), anno.name()));
62+
//提示:不支持CloudJob拦截器
63+
XxlJobExecutor.registryJobHandler(name, bw.raw());
64+
});
65+
66+
//注册构建器和提取器
67+
context.beanExtractorAdd(XxlJob.class, new XxlJob3Extractor());
68+
69+
//构建自动配置
70+
context.beanMake(XxlJob3AutoConfig.class);
71+
72+
Solon.app().onEvent(AppLoadEndEvent.class, e -> {
73+
XxlJobExecutor executor = context.getBean(XxlJobExecutor.class);
74+
executor.start();
75+
});
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2017-2025 noear.org and authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.noear.solon.cloud.extend.xxljob3.integration;
17+
18+
import com.xxl.job.core.executor.XxlJobExecutor;
19+
import com.xxl.job.core.handler.annotation.XxlJob;
20+
import com.xxl.job.core.handler.impl.MethodJobHandler;
21+
import org.noear.solon.core.BeanExtractor;
22+
import org.noear.solon.core.BeanWrap;
23+
24+
import java.lang.reflect.Method;
25+
26+
/**
27+
* @author noear
28+
* @since 1.4
29+
*/
30+
class XxlJob3Extractor implements BeanExtractor<XxlJob> {
31+
@Override
32+
public void doExtract(BeanWrap bw, Method method, XxlJob anno) {
33+
String name = anno.value();
34+
35+
if (name.trim().length() == 0) {
36+
throw new IllegalStateException("xxl-job method-jobhandler name invalid, for[" + bw.clz() + "#" + method.getName() + "] .");
37+
}
38+
if (XxlJobExecutor.loadJobHandler(name) != null) {
39+
throw new IllegalStateException("xxl-job jobhandler[" + name + "] naming conflicts.");
40+
}
41+
42+
43+
method.setAccessible(true);
44+
45+
// init and destroy
46+
Method initMethod = null;
47+
Method destroyMethod = null;
48+
49+
if (anno.init().trim().length() > 0) {
50+
try {
51+
initMethod = bw.clz().getDeclaredMethod(anno.init());
52+
initMethod.setAccessible(true);
53+
} catch (NoSuchMethodException e) {
54+
throw new IllegalStateException("xxl-job method-jobhandler initMethod invalid, for[" + bw.clz() + "#" + method.getName() + "] .");
55+
}
56+
}
57+
if (anno.destroy().trim().length() > 0) {
58+
try {
59+
destroyMethod = bw.clz().getDeclaredMethod(anno.destroy());
60+
destroyMethod.setAccessible(true);
61+
} catch (NoSuchMethodException e) {
62+
throw new IllegalStateException("xxl-job method-jobhandler destroyMethod invalid, for[" + bw.clz() + "#" + method.getName() + "] .");
63+
}
64+
}
65+
66+
//提示:不支持CloudJob拦截器
67+
XxlJobExecutor.registryJobHandler(name, new MethodJobHandler(bw.raw(), method, initMethod, destroyMethod));
68+
}
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2017-2025 noear.org and authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.noear.solon.cloud.extend.xxljob3.service;
17+
18+
import com.xxl.job.core.executor.XxlJobExecutor;
19+
import org.noear.solon.cloud.CloudJobHandler;
20+
import org.noear.solon.cloud.extend.xxljob3.service.IJobHandlerImpl;
21+
import org.noear.solon.cloud.model.JobHolder;
22+
import org.noear.solon.cloud.service.CloudJobService;
23+
import org.noear.solon.logging.utils.TagsMDC;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
27+
/**
28+
* @author noear
29+
* @since 1.4
30+
*/
31+
public class CloudJobServiceImpl implements CloudJobService {
32+
static final Logger log = LoggerFactory.getLogger(CloudJobServiceImpl.class);
33+
34+
@Override
35+
public boolean register(String name, String cron7x, String description, CloudJobHandler handler) {
36+
JobHolder jobHolder = new JobHolder(name, cron7x, description, handler);
37+
38+
XxlJobExecutor.registryJobHandler(name, new IJobHandlerImpl(jobHolder));
39+
40+
TagsMDC.tag0("CloudJob");
41+
log.info("CloudJob: Handler registered name:{}, class:{}", name, handler.getClass().getName());
42+
TagsMDC.tag0("");
43+
return true;
44+
}
45+
46+
@Override
47+
public boolean isRegistered(String name) {
48+
return XxlJobExecutor.loadJobHandler(name) != null;
49+
}
50+
}

0 commit comments

Comments
 (0)