---初始化项目

This commit is contained in:
2025-09-19 16:14:08 +08:00
parent 902d3d7e3b
commit afee7c03ac
767 changed files with 75809 additions and 82 deletions

98
powerjob-common/pom.xml Normal file
View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>powerjob</artifactId>
<groupId>tech.powerjob</groupId>
<version>5.1.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>powerjob-common</artifactId>
<name>powerjob-common</name>
<version>5.1.2</version>
<packaging>jar</packaging>
<properties>
<slf4j.version>1.7.36</slf4j.version>
<commons.lang.version>3.12.0</commons.lang.version>
<commons.io.version>2.11.0</commons.io.version>
<guava.version>31.1-jre</guava.version>
<okhttp.version>3.14.9</okhttp.version>
<kryo.version>5.3.0</kryo.version>
<jackson.version>2.14.3</jackson.version>
<junit.version>5.9.0</junit.version>
</properties>
<dependencies>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang.version}</version>
</dependency>
<!-- guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<!-- OKHttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<!-- commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<!-- kryo 超超超高性能序列化框架 -->
<dependency>
<groupId>com.esotericsoftware.kryo</groupId>
<artifactId>kryo5</artifactId>
<version>${kryo.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- 解决 Java8 data/time 类型处理问题 #869 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- Junit tests -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,24 @@
package tech.powerjob.common;
/**
* Container constants.
*
* @author tjq
* @since 2020/5/15
*/
public class ContainerConstant {
/**
* Spring-context configuration file name of the container.
*/
public static final String SPRING_CONTEXT_FILE_NAME = "oms-worker-container-spring-context.xml";
/**
* Property file name of the container.
*/
public static final String CONTAINER_PROPERTIES_FILE_NAME = "oms-worker-container.properties";
/**
* Package name of the container.
*/
public static final String CONTAINER_PACKAGE_NAME_KEY = "PACKAGE_NAME";
}

View File

@ -0,0 +1,35 @@
package tech.powerjob.common;
/**
* Common constants.
*
* @author tjq
* @since 2020/5/31
*/
public class OmsConstant {
/**
* package name
*/
public static final String PACKAGE = "tech.powerjob";
public static final int SERVER_DEFAULT_AKKA_PORT = 10086;
public static final int SERVER_DEFAULT_HTTP_PORT = 10010;
public static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static final String TIME_PATTERN_PLUS = "yyyy-MM-dd HH:mm:ss.SSS";
public static final String NONE = "N/A";
public static final String COMMA = ",";
public static final String AND = "&";
public static final String EQUAL = "=";
public static final String LINE_SEPARATOR = "\r\n";
public static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type";
public static final String JSON_MEDIA_TYPE = "application/json; charset=utf-8";
public static final String NULL = "null";
}

View File

@ -0,0 +1,70 @@
package tech.powerjob.common;
/**
* OpenAPI 常量
*
* @author tjq
* @since 2020/4/15
*/
public class OpenAPIConstant {
private OpenAPIConstant(){
}
public static final String WEB_PATH = "/openApi";
public static final String ASSERT = "/assert";
public static final String AUTH_APP = "/authApp";
/* ************* JOB 区 ************* */
public static final String SAVE_JOB = "/saveJob";
public static final String COPY_JOB = "/copyJob";
public static final String EXPORT_JOB = "/exportJob";
public static final String FETCH_JOB = "/fetchJob";
public static final String FETCH_ALL_JOB = "/fetchAllJob";
public static final String QUERY_JOB = "/queryJob";
public static final String DISABLE_JOB = "/disableJob";
public static final String ENABLE_JOB = "/enableJob";
public static final String DELETE_JOB = "/deleteJob";
public static final String RUN_JOB = "/runJob";
public static final String RUN_JOB2 = "/runJob2";
/* ************* Instance 区 ************* */
public static final String STOP_INSTANCE = "/stopInstance";
public static final String CANCEL_INSTANCE = "/cancelInstance";
public static final String RETRY_INSTANCE = "/retryInstance";
public static final String FETCH_INSTANCE_STATUS = "/fetchInstanceStatus";
public static final String FETCH_INSTANCE_INFO = "/fetchInstanceInfo";
public static final String QUERY_INSTANCE = "/queryInstance";
/* ************* Workflow 区 ************* */
public static final String SAVE_WORKFLOW = "/saveWorkflow";
public static final String COPY_WORKFLOW = "/copyWorkflow";
public static final String FETCH_WORKFLOW = "/fetchWorkflow";
public static final String DISABLE_WORKFLOW = "/disableWorkflow";
public static final String ENABLE_WORKFLOW = "/enableWorkflow";
public static final String DELETE_WORKFLOW = "/deleteWorkflow";
public static final String RUN_WORKFLOW = "/runWorkflow";
public static final String SAVE_WORKFLOW_NODE = "/addWorkflowNode";
/* ************* WorkflowInstance 区 ************* */
public static final String STOP_WORKFLOW_INSTANCE = "/stopWfInstance";
public static final String RETRY_WORKFLOW_INSTANCE = "/retryWfInstance";
public static final String FETCH_WORKFLOW_INSTANCE_INFO = "/fetchWfInstanceInfo";
public static final String MARK_WORKFLOW_NODE_AS_SUCCESS = "/markWorkflowNodeAsSuccess";
/* ************* 鉴权 ************* */
public static final String REQUEST_HEADER_ACCESS_TOKEN = "X-POWERJOB-ACCESS-TOKEN";
public static final String REQUEST_HEADER_APP_ID = "X-POWERJOB-APP-ID";
public static final String RESPONSE_HEADER_AUTH_STATUS = "X-POWERJOB-AUTH-PASSED";
}

View File

@ -0,0 +1,80 @@
package tech.powerjob.common;
import java.net.NetworkInterface;
/**
* 通过 JVM 启动参数传入的配置信息
*
* @author tjq
* @since 2020/8/8
*/
public class PowerJobDKey {
/**
* The property name for {@link NetworkInterface#getDisplayName() the name of network interface} that the PowerJob application prefers
*/
public static final String PREFERRED_NETWORK_INTERFACE = "powerjob.network.interface.preferred";
/**
* 绑定地址,一般填写本机网卡地址
*/
public static final String BIND_LOCAL_ADDRESS = "powerjob.network.local.address";
/**
* 外部地址,可选,默认与绑定地址相同。当存在 NAT 等场景时可通过单独传递外部地址来实现通讯
*/
public static final String NT_EXTERNAL_ADDRESS = "powerjob.network.external.address";
public static final String NT_EXTERNAL_PORT = "powerjob.network.external.port";
/**
* Java regular expressions for network interfaces that will be ignored.
*/
public static final String IGNORED_NETWORK_INTERFACE_REGEX = "powerjob.network.interface.ignored";
/**
* Enables compression during data transfer, such as gzip under the HTTP protocol. default value is 'false'
* Note that enabling compression reduces network usage, but increases CPU consumption
*/
public static final String TRANSPORTER_USE_COMPRESSING = "powerjob.transporter.compression.enabled";
/**
* keep-alive connection timeout(in seconds), value <= 0 means disable keepalive. default value is 75
*/
public static final String TRANSPORTER_KEEP_ALIVE_TIMEOUT = "powerjob.transporter.keepalive.timeout";
public static final String WORKER_STATUS_CHECK_PERIOD = "powerjob.worker.status-check.normal.period";
/**
* allowed PowerJob to invoke Thread#stop to kill a thread when PowerJob can't interrupt the thread
* <a href="https://stackoverflow.com/questions/16504140/thread-stop-deprecated">It's VERY dangerous</a>
*/
public static final String WORKER_ALLOWED_FORCE_STOP_THREAD = "powerjob.worker.allowed-force-stop-thread";
public static final String WORKER_WORK_SPACE = "powerjob.worker.workspace";
/**
* ms
*/
public static final String FREQUENCY_JOB_MAX_INTERVAL = "powerjob.server.frequency-job.max-interval";
/* ******************* 系统纬度参数,低频使用 ******************* */
/**
* 自行指定可用CPU核数
*/
public static final String SYS_AVAILABLE_PROCESSORS = "powerjob.system.available-processors";
/* ******************* 不太可能有人用的参数,主要方便内部测试 ******************* */
/**
* 最大活跃任务数量,超出部分 SWAP 到磁盘以提升性能
*/
public static final String WORKER_RUNTIME_SWAP_MAX_ACTIVE_TASK_NUM = "powerjob.worker.swap.max-active-task-num";
public static final String WORKER_RUNTIME_SWAP_TASK_SCHEDULE_INTERVAL_MS = "powerjob.worker.swap.scan-interval";
public static final String SERVER_TEST_ACCOUNT_USERNAME = "powerjob.server.test-accounts";
/**
* 特殊环境test 代表测试环境trial 代表试用环境
*/
public static final String SP_ENV = "powerjob.sp-env";
}

View File

@ -0,0 +1,41 @@
package tech.powerjob.common;
import lombok.Getter;
import lombok.Setter;
/**
* PowerJob Query interface
*
* @author tjq
* @since 2021/1/15
*/
@Getter
@Setter
public abstract class PowerQuery {
public static String EQUAL = "Eq";
public static String NOT_EQUAL = "NotEq";
public static String LIKE = "Like";
public static String NOT_LIKE = "NotLike";
public static String LESS_THAN = "Lt";
public static String LESS_THAN_EQUAL = "LtEq";
public static String GREATER_THAN = "Gt";
public static String GREATER_THAN_EQUAL = "GtEq";
public static String IN = "In";
public static String NOT_IN = "NotIn";
public static String IS_NULL = "IsNull";
public static String IS_NOT_NULL = "IsNotNull";
private Long appIdEq;
}

View File

@ -0,0 +1,12 @@
package tech.powerjob.common;
import java.io.Serializable;
/**
* PowerJob serializable interface.
*
* @author tjq
* @since 2020/4/16
*/
public interface PowerSerializable extends Serializable {
}

View File

@ -0,0 +1,104 @@
package tech.powerjob.common;
/**
* RemoteConstant
*
* @author tjq
* @since 2020/3/17
*/
public class RemoteConstant {
/* ************************ AKKA WORKER ************************ */
public static final int DEFAULT_WORKER_PORT = 27777;
/* ************************ OTHERS ************************ */
public static final String EMPTY_ADDRESS = "N/A";
public static final long DEFAULT_TIMEOUT_MS = 5000;
/* ************************ SERVER-self_side (s4s == server for server side) ************************ */
public static final String S4S_PATH = "friend";
/**
* server 集群间的心跳处理
*/
public static final String S4S_HANDLER_PING = "ping";
/**
* 处理其他 server 的执行请求
*/
public static final String S4S_HANDLER_PROCESS = "process";
/* ************************ SERVER-worker_sides4w == server for worker side ************************ */
public static final String S4W_PATH = "server";
/**
* server 处理在线日志
*/
public static final String S4W_HANDLER_REPORT_LOG = "reportLog";
/**
* server 处理 worker 心跳
*/
public static final String S4W_HANDLER_WORKER_HEARTBEAT = "workerHeartbeat";
/**
* server 处理 TaskTracker 上报的任务实例状态
*/
public static final String S4W_HANDLER_REPORT_INSTANCE_STATUS = "reportInstanceStatus";
/**
* server 查询任务的可执行集群
*/
public static final String S4W_HANDLER_QUERY_JOB_CLUSTER = "queryJobCluster";
/**
* server 处理 worker 请求部署容器命令
*/
public static final String S4W_HANDLER_WORKER_NEED_DEPLOY_CONTAINER = "queryContainer";
/* ************************ Worker-TaskTracker ************************ */
public static final String WTT_PATH = "taskTracker";
/**
* server 任务执行命令
*/
public static final String WTT_HANDLER_RUN_JOB = "runJob";
/**
* server 停止任务实例命令
*/
public static final String WTT_HANDLER_STOP_INSTANCE = "stopInstance";
/**
* sever 查询任务状态
*/
public static final String WTT_HANDLER_QUERY_INSTANCE_STATUS = "queryInstanceStatus";
/**
* PT 上报任务状态,包含执行结果
*/
public static final String WTT_HANDLER_REPORT_TASK_STATUS = "reportTaskStatus";
/**
* PT 上报自身状态
*/
public static final String WTT_HANDLER_REPORT_PROCESSOR_TRACKER_STATUS = "reportProcessorTrackerStatus";
/**
* Map 任务
*/
public static final String WTT_HANDLER_MAP_TASK = "mapTask";
/* ************************ Worker-ProcessorTracker ************************ */
public static final String WPT_PATH = "processorTracker";
public static final String WPT_HANDLER_START_TASK = "startTask";
public static final String WPT_HANDLER_STOP_INSTANCE = "stopInstance";
/* ************************ Worker-NORMAL ************************ */
public static final String WORKER_PATH = "worker";
public static final String WORKER_HANDLER_DEPLOY_CONTAINER = "deployContainer";
public static final String WORKER_HANDLER_DESTROY_CONTAINER = "destroyContainer";
}

View File

@ -0,0 +1,91 @@
package tech.powerjob.common;
/**
* 系统生成的任务实例运行结果
*
* @author tjq
* @since 2020/4/11
*/
public class SystemInstanceResult {
private SystemInstanceResult() {
}
/* *********** 普通instance 专用 *********** */
/**
* 同时运行的任务实例数过多
*/
public static final String TOO_MANY_INSTANCES = "too many instances(%d>%d)";
/**
* 无可用worker
*/
public static final String NO_WORKER_AVAILABLE = "no worker available";
/**
* 任务执行超时
*/
public static final String INSTANCE_EXECUTE_TIMEOUT = "instance execute timeout";
/**
* 任务执行超时,成功打断任务
*/
public static final String INSTANCE_EXECUTE_TIMEOUT_INTERRUPTED = "instance execute timeout,interrupted success";
/**
* 任务执行超时,强制终止任务
*/
public static final String INSTANCE_EXECUTE_TIMEOUT_FORCE_STOP= "instance execute timeout,force stop success";
/**
* 用户手动停止任务,成功打断任务
*/
public static final String USER_STOP_INSTANCE_INTERRUPTED= "user stop instance,interrupted success";
/**
* 用户手动停止任务,被系统强制终止
*/
public static final String USER_STOP_INSTANCE_FORCE_STOP= "user stop instance,force stop success";
/**
* 创建根任务失败
*/
public static final String TASK_INIT_FAILED = "create root task failed";
/**
* 未知错误
*/
public static final String UNKNOWN_BUG = "unknown bug";
/**
* TaskTracker 长时间未上报
*/
public static final String REPORT_TIMEOUT = "worker report timeout, maybe TaskTracker down";
public static final String CAN_NOT_FIND_JOB_INFO = "can't find job info";
/* *********** workflow 专用 *********** */
public static final String MIDDLE_JOB_FAILED = "middle job failed";
public static final String MIDDLE_JOB_STOPPED = "middle job stopped by user";
public static final String CAN_NOT_FIND_JOB = "can't find some job";
public static final String CAN_NOT_FIND_NODE = "can't find some node";
public static final String ILLEGAL_NODE = "illegal node info";
/**
* 没有启用的节点
*/
public static final String NO_ENABLED_NODES = "no enabled nodes";
/**
* 被用户手动停止
*/
public static final String STOPPED_BY_USER = "stopped by user";
public static final String CANCELED_BY_USER = "canceled by user";
/**
* 无效 DAG
*/
public static final String INVALID_DAG = "invalid dag";
/**
* 被禁用的节点
*/
public static final String DISABLE_NODE = "disable node";
/**
* 标记为成功的节点
*/
public static final String MARK_AS_SUCCESSFUL_NODE = "mark as successful node";
}

View File

@ -0,0 +1,18 @@
package tech.powerjob.common;
/**
* 工作流上下文相关常量
*
* @author Echo009
* @since 2021/2/3
*/
public final class WorkflowContextConstant {
/**
* 上下文初始参数
*/
public static final String CONTEXT_INIT_PARAMS_KEY = "initParams";
}

View File

@ -0,0 +1,26 @@
package tech.powerjob.common.enhance;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ScheduledExecutorService;
/**
* 安全的 runnable可防止因抛出异常导致周期性任务终止
* 使用 {@link ScheduledExecutorService} 执行任务时,推荐继承此类捕获并打印异常,避免因为抛出异常导致周期性任务终止
*
* @author songyinyin
* @since 2023/9/20 15:52
*/
@Slf4j
public abstract class SafeRunnable implements Runnable{
@Override
public void run() {
try {
run0();
} catch (Exception e) {
log.error("[SafeRunnable] run failed", e);
}
}
protected abstract void run0();
}

View File

@ -0,0 +1,30 @@
package tech.powerjob.common.enhance;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ScheduledExecutorService;
/**
* 使用 {@link ScheduledExecutorService} 执行任务时,推荐使用此对象包装一层,避免因为抛出异常导致周期性任务终止
*
* @author songyinyin
* @since 2023/9/20 16:04
*/
@Slf4j
public class SafeRunnableWrapper implements Runnable {
private final Runnable runnable;
public SafeRunnableWrapper(Runnable runnable) {
this.runnable = runnable;
}
@Override
public void run() {
try {
runnable.run();
} catch (Exception e) {
log.error("[SafeRunnableWrapper] run failed", e);
}
}
}

View File

@ -0,0 +1,43 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* DispatchStrategy
*
* @author tjq
* @since 2021/2/22
*/
@Getter
@AllArgsConstructor
public enum DispatchStrategy {
/**
* 健康度优先
*/
HEALTH_FIRST(1),
/**
* 随机
*/
RANDOM(2),
/**
* 指定执行
*/
SPECIFY(11)
;
private final int v;
public static DispatchStrategy of(Integer v) {
if (v == null) {
return HEALTH_FIRST;
}
for (DispatchStrategy ds : values()) {
if (v.equals(ds.v)) {
return ds;
}
}
throw new IllegalArgumentException("unknown DispatchStrategy of " + v);
}
}

View File

@ -0,0 +1,22 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 加密类型
*
* @author tjq
* @since 2024/8/10
*/
@Getter
@AllArgsConstructor
public enum EncryptType {
NONE("none"),
MD5("md5")
;
private final String code;
}

View File

@ -0,0 +1,22 @@
package tech.powerjob.common.enums;
/**
* Environment Enum class.
*
* @author tjq
* @since 2020/5/3
*/
public enum Env {
/**
* Development or test environment.
*/
DAILY,
/**
* Pre-release environment.
*/
PRE,
/**
* Production environment.
*/
PRODUCT
}

View File

@ -0,0 +1,75 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 鉴权错误信息
*
* @author tjq
* @since 2024/2/11
*/
@Getter
@AllArgsConstructor
public enum ErrorCodes {
USER_NOT_LOGIN("-100", "UserNotLoggedIn"),
USER_NOT_EXIST("-101", "UserNotExist"),
USER_AUTH_FAILED("-102", "UserAuthFailed"),
/**
* 账户被停用
*/
USER_DISABLED("-103", "UserDisabled"),
NO_PERMISSION("-200", "NoPermission"),
/**
* 无效请求,一般是参数问题
*/
INVALID_REQUEST("-300", "INVALID_REQUEST"),
INCORRECT_PASSWORD("-400", "INCORRECT_PASSWORD"),
/**
* 非法令牌
*/
INVALID_TOKEN("-401", "INVALID_TOKEN"),
/**
* 无效 APP无法找到 app
*/
INVALID_APP("-402", "INVALID_APP"),
/**
* 令牌过期
*/
TOKEN_EXPIRED("-403", "TOKEN_EXPIRED"),
/**
* 系统内部异常
*/
SYSTEM_UNKNOWN_ERROR("-500", "SYS_UNKNOWN_ERROR"),
/**
* 非法参数
*/
ILLEGAL_ARGS_ERROR("-501", "ILLEGAL_ARGS_ERROR"),
/**
* 不允许操作
*/
OPERATION_NOT_PERMITTED("-502", "OPERATION_NOT_PERMITTED"),
/**
* OPENAPI 错误码号段 -10XX
*/
OPEN_API_AUTH_FAILED("-1002", "OPEN_API_AUTH_FAILED"),
/**
* PowerJobClient 错误码号段
*/
CLIENT_HTTP_REQUEST_FAILED("-2001", "CLIENT_HTTP_REQUEST_FAILED"),
;
private final String code;
private final String msg;
}

View File

@ -0,0 +1,40 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Execution type.
*
* @author tjq
* @since 2020/3/17
*/
@Getter
@AllArgsConstructor
public enum ExecuteType {
/**
* Standalone type of task.
*/
STANDALONE(1, "单机执行"),
/**
* Broadcast type of task.
*/
BROADCAST(2, "广播执行"),
/**
* MapReduce type of task.
*/
MAP_REDUCE(3, "MapReduce"),
MAP(4, "Map");
private final int v;
private final String des;
public static ExecuteType of(int v) {
for (ExecuteType type : values()) {
if (type.v == v) {
return type;
}
}
throw new IllegalArgumentException("unknown ExecuteType of " + v);
}
}

View File

@ -0,0 +1,49 @@
package tech.powerjob.common.enums;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
/**
* Status of the job instance
*
* @author tjq
* @since 2020/3/17
*/
@Getter
@AllArgsConstructor
public enum InstanceStatus {
/**
*
*/
WAITING_DISPATCH(1, "等待派发"),
WAITING_WORKER_RECEIVE(2, "等待Worker接收"),
RUNNING(3, "运行中"),
FAILED(4, "失败"),
SUCCEED(5, "成功"),
CANCELED(9, "取消"),
STOPPED(10, "手动停止");
private final int v;
private final String des;
/**
* 广义的运行状态
*/
public static final List<Integer> GENERALIZED_RUNNING_STATUS = Lists.newArrayList(WAITING_DISPATCH.v, WAITING_WORKER_RECEIVE.v, RUNNING.v);
/**
* 结束状态
*/
public static final List<Integer> FINISHED_STATUS = Lists.newArrayList(FAILED.v, SUCCEED.v, CANCELED.v, STOPPED.v);
public static InstanceStatus of(int v) {
for (InstanceStatus is : values()) {
if (v == is.v) {
return is;
}
}
throw new IllegalArgumentException("InstanceStatus has no item for value " + v);
}
}

View File

@ -0,0 +1,35 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* 日志级别
*
* @author tjq
* @since 12/20/20
*/
@Getter
@AllArgsConstructor
public enum LogLevel {
DEBUG(1),
INFO(2),
WARN(3),
ERROR(4),
OFF(99);
private final int v;
public static String genLogLevelString(Integer v) {
for (LogLevel logLevel : values()) {
if (Objects.equals(logLevel.v, v)) {
return logLevel.name();
}
}
return "UNKNOWN";
}
}

View File

@ -0,0 +1,37 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* LogType
*
* @author tjq
* @since 2022/10/3
*/
@Getter
@AllArgsConstructor
public enum LogType {
ONLINE(1),
LOCAL(2),
STDOUT(3),
LOCAL_AND_ONLINE(4),
NULL(999);
private final Integer v;
public static LogType of(Integer type) {
if (type == null) {
return ONLINE;
}
for (LogType logType : values()) {
if (logType.v.equals(type)) {
return logType;
}
}
return ONLINE;
}
}

View File

@ -0,0 +1,22 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types">消息内容类型</a>
*
* @author tjq
* @since 2024/8/10
*/
@Getter
@AllArgsConstructor
public enum MIME {
APPLICATION_JSON("application/json; charset=utf-8"),
APPLICATION_FORM("application/x-www-form-urlencoded")
;
private final String code;
}

View File

@ -0,0 +1,35 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Task Processor Type
*
* @author tjq
* @since 2020/3/23
*/
@Getter
@AllArgsConstructor
public enum ProcessorType {
BUILT_IN(1, "内建处理器"),
EXTERNAL(4, "外部处理器(动态加载)"),
@Deprecated
SHELL(2, "SHELL脚本"),
@Deprecated
PYTHON(3, "Python脚本");
private final int v;
private final String des;
public static ProcessorType of(int v) {
for (ProcessorType type : values()) {
if (type.v == v) {
return type;
}
}
throw new IllegalArgumentException("unknown ProcessorType of " + v);
}
}

View File

@ -0,0 +1,30 @@
package tech.powerjob.common.enums;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
/**
* transport protocol between server and worker
*
* @author tjq
* @since 2021/2/7
*/
@Getter
public enum Protocol {
AKKA,
HTTP,
MU;
public static Protocol of(String protocol) {
if (StringUtils.isEmpty(protocol)) {
return AKKA;
}
try {
return Protocol.valueOf(protocol.toUpperCase());
} catch (Exception ignore) {
}
// For compatibility with older versions, the AKKA protocol is used by default
return AKKA;
}
}

View File

@ -0,0 +1,27 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 特殊环境
*
* @author tjq
* @since 2025/8/17
*/
@Getter
@AllArgsConstructor
public enum SpEnv {
/**
* 测试环境
*/
TEST("test"),
/**
* 试用环境
*/
TRIAL("trial")
;
private final String code;
}

View File

@ -0,0 +1,38 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 支持开/关的状态,如 任务状态JobStatus和工作流状态WorkflowStatus
*
* @author tjq
* @since 2020/4/6
*/
@Getter
@AllArgsConstructor
public enum SwitchableStatus {
/**
* 启用
*/
ENABLE(1),
/**
* 关闭
*/
DISABLE(2),
/**
* 软删除
*/
DELETED(99);
private final int v;
public static SwitchableStatus of(int v) {
for (SwitchableStatus type : values()) {
if (type.v == v) {
return type;
}
}
throw new IllegalArgumentException("unknown SwitchableStatus of " + v);
}
}

View File

@ -0,0 +1,42 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* TaskTracker 行为枚举
*
* @author tjq
* @since 2024/2/24
*/
@Getter
@AllArgsConstructor
public enum TaskTrackerBehavior {
/**
* 普通:不特殊处理,参与集群计算,会导致 TaskTracker 负载比常规节点高。适用于节点数不那么多,任务不那么繁重的场景
*/
NORMAL(1),
/**
* 划水:只负责管理节点,不参与计算,稳定性最优。适用于节点数量非常多的大规模计算场景,少一个计算节点来换取稳定性提升
*/
PADDLING(11)
;
private final Integer v;
public static TaskTrackerBehavior of(Integer type) {
if (type == null) {
return NORMAL;
}
for (TaskTrackerBehavior t : values()) {
if (t.v.equals(type)) {
return t;
}
}
return NORMAL;
}
}

View File

@ -0,0 +1,46 @@
package tech.powerjob.common.enums;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import java.util.Collections;
import java.util.List;
/**
* Scheduling time strategies
*
* @author tjq
* @since 2020/3/30
*/
@Getter
@AllArgsConstructor
@ToString
public enum TimeExpressionType {
API(1),
CRON(2),
FIXED_RATE(3),
FIXED_DELAY(4),
WORKFLOW(5),
DAILY_TIME_INTERVAL(11);
private final int v;
public static final List<Integer> FREQUENT_TYPES = Collections.unmodifiableList(Lists.newArrayList(FIXED_RATE.v, FIXED_DELAY.v));
/**
* 首次计算触发时间时必须计算出一个有效值
*/
public static final List<Integer> INSPECT_TYPES = Collections.unmodifiableList(Lists.newArrayList(CRON.v, DAILY_TIME_INTERVAL.v));
public static TimeExpressionType of(int v) {
for (TimeExpressionType type : values()) {
if (type.v == v) {
return type;
}
}
throw new IllegalArgumentException("unknown TimeExpressionType of " + v);
}
}

View File

@ -0,0 +1,49 @@
package tech.powerjob.common.enums;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Collections;
import java.util.List;
/**
* Workflow 任务运行状态
*
* @author tjq
* @since 2020/5/26
*/
@Getter
@AllArgsConstructor
public enum WorkflowInstanceStatus {
/**
* 初始状态为等待调度
*/
WAITING(1, "等待调度"),
RUNNING(2, "运行中"),
FAILED(3, "失败"),
SUCCEED(4, "成功"),
STOPPED(10, "手动停止");
/**
* 广义的运行状态
*/
public static final List<Integer> GENERALIZED_RUNNING_STATUS = Collections.unmodifiableList(Lists.newArrayList(WAITING.v, RUNNING.v));
/**
* 结束状态
*/
public static final List<Integer> FINISHED_STATUS = Collections.unmodifiableList(Lists.newArrayList(FAILED.v, SUCCEED.v, STOPPED.v));
private final int v;
private final String des;
public static WorkflowInstanceStatus of(int v) {
for (WorkflowInstanceStatus is : values()) {
if (v == is.v) {
return is;
}
}
throw new IllegalArgumentException("WorkflowInstanceStatus has no item for value " + v);
}
}

View File

@ -0,0 +1,47 @@
package tech.powerjob.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 节点类型
*
* @author Echo009
* @since 2021/3/7
*/
@Getter
@AllArgsConstructor
public enum WorkflowNodeType {
/**
* 任务节点
*/
JOB(1,false),
/**
* 判断节点
*/
DECISION(2,true),
/**
* 内嵌工作流
*/
NESTED_WORKFLOW(3,false),
;
private final int code;
/**
* 控制节点
*/
private final boolean controlNode;
public static WorkflowNodeType of(int code) {
for (WorkflowNodeType nodeType : values()) {
if (nodeType.code == code) {
return nodeType;
}
}
throw new IllegalArgumentException("unknown WorkflowNodeType of " + code);
}
}

View File

@ -0,0 +1,10 @@
package tech.powerjob.common.exception;
/**
* ImpossibleException
*
* @author tjq
* @since 2023/7/12
*/
public class ImpossibleException extends RuntimeException {
}

View File

@ -0,0 +1,29 @@
package tech.powerjob.common.exception;
/**
* PowerJob 受检异常,需要开发者手动处理
*
* @author KFC·D·Fans
* @since 2021/3/21
*/
public class PowerJobCheckedException extends Exception {
public PowerJobCheckedException() {
}
public PowerJobCheckedException(String message) {
super(message);
}
public PowerJobCheckedException(String message, Throwable cause) {
super(message, cause);
}
public PowerJobCheckedException(Throwable cause) {
super(cause);
}
public PowerJobCheckedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@ -0,0 +1,42 @@
package tech.powerjob.common.exception;
import lombok.Getter;
import lombok.Setter;
import tech.powerjob.common.enums.ErrorCodes;
/**
* PowerJob 运行时异常
*
* @author tjq
* @since 2020/5/26
*/
@Setter
@Getter
public class PowerJobException extends RuntimeException {
protected String code;
public PowerJobException() {
}
public PowerJobException(String message) {
super(message);
}
public PowerJobException(ErrorCodes errorCode, String extraMsg) {
super(extraMsg == null ? errorCode.getMsg() : errorCode.getMsg().concat(":").concat(extraMsg));
this.code = errorCode.getCode();
}
public PowerJobException(String message, Throwable cause) {
super(message, cause);
}
public PowerJobException(Throwable cause) {
super(cause);
}
public PowerJobException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@ -0,0 +1,17 @@
package tech.powerjob.common.exception;
import tech.powerjob.common.enums.ErrorCodes;
/**
* PowerJobExceptionLauncher
*
* @author tjq
* @since 2024/11/22
*/
public class PowerJobExceptionLauncher {
public PowerJobExceptionLauncher(ErrorCodes errorCode, String message) {
throw new PowerJobException(errorCode, message);
}
}

View File

@ -0,0 +1,28 @@
package tech.powerjob.common.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Echo009
* @since 2022/1/25
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AlarmConfig {
/**
* 触发告警的阈值
*/
private Integer alertThreshold;
/**
* 统计的窗口长度s
*/
private Integer statisticWindowLen;
/**
* 沉默时间窗口s
*/
private Integer silenceWindowLen;
}

View File

@ -0,0 +1,35 @@
package tech.powerjob.common.model;
import tech.powerjob.common.PowerSerializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Deployed Container Information
*
* @author tjq
* @since 2020/5/18
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeployedContainerInfo implements PowerSerializable {
/**
* Id of the container.
*/
private Long containerId;
/**
* Version of the container.
*/
private String version;
/**
* Deploy timestamp.
*/
private long deployedTime;
/**
* No need to report to the server
*/
private String workerAddress;
}

View File

@ -0,0 +1,29 @@
package tech.powerjob.common.model;
import lombok.Data;
/**
* The class for Git Repository info.
*
* @author tjq
* @since 2020/5/17
*/
@Data
public class GitRepoInfo {
/**
* Address of Git repository.
*/
private String repo;
/**
* Name of the branch.
*/
private String branch;
/**
* username of Git.
*/
private String username;
/**
* Password of Git.
*/
private String password;
}

View File

@ -0,0 +1,121 @@
package tech.powerjob.common.model;
import lombok.Data;
import lombok.NoArgsConstructor;
import tech.powerjob.common.PowerSerializable;
import java.util.List;
/**
* Detailed info of job instances.
*
* @author tjq
* @since 2020/4/11
*/
@Data
@NoArgsConstructor
public class InstanceDetail implements PowerSerializable {
/**
* Expected trigger time.
*/
private Long expectedTriggerTime;
/**
* Actual trigger time of an instance.
*/
private Long actualTriggerTime;
/**
* Finish time of an instance, which may be null.
*/
private Long finishedTime;
/**
* Status of the task instance.
*/
private Integer status;
/**
* Execution result, which may be null.
*/
private String result;
/**
* Task tracker address.
*/
private String taskTrackerAddress;
/**
* 任务参数
*/
private String jobParams;
/**
* Param string that is passed to an instance when it is initialized.
*/
private String instanceParams;
/**
* “外键”,用于 OPENAPI 场景业务场景与 PowerJob 实例的绑定
*/
private String outerKey;
/**
* 扩展属性,用于 OPENAPI 场景上下文参数的透传
*/
private String extendValue;
/**
* Task detail, used by MapReduce or Broadcast tasks.
* 命名有点问题,实际是 task 统计信息
*/
private TaskDetail taskDetail;
/**
* 查询出来的 Task 详细结果
*/
private List<TaskDetailInfo> queriedTaskDetailInfoList;
/**
* Sub instance details, used by frequent tasks.
*/
private List<SubInstanceDetail> subInstanceDetails;
/**
* Running times.
*/
private Long runningTimes;
/**
* Extended fields. Middlewares are not supposed to update frequently.
* Changes in PowerJob-common may lead to incompatible versions.
* PowerJob-common packages should not be modified if not necessary.
*/
private String extra;
/**
* Extra info for frequent tasks, return List<SubInstanceDetail>.
*/
@Data
@NoArgsConstructor
public static class SubInstanceDetail implements PowerSerializable {
private long subInstanceId;
private Long startTime;
private Long finishedTime;
private String result;
private int status;
}
/**
* Extra info of {@code MapReduce} or {@code Broadcast} type of tasks.
*/
@Data
@NoArgsConstructor
public static class TaskDetail implements PowerSerializable {
private long totalTaskNum;
private long succeedTaskNum;
private long failedTaskNum;
// 等待派发状态(仅存在 TaskTracker 数据库中)
protected Long waitingDispatchTaskNum;
// 已派发,但 ProcessorTracker 未确认,可能由于网络错误请求未送达,也有可能 ProcessorTracker 线程池满,拒绝执行
protected Long workerUnreceivedTaskNum;
// ProcessorTracker确认接收存在与线程池队列中排队执行
protected Long receivedTaskNum;
// ProcessorTracker正在执行
protected Long runningTaskNum;
}
}

View File

@ -0,0 +1,36 @@
package tech.powerjob.common.model;
import tech.powerjob.common.PowerSerializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Log instance model.
*
* @author tjq
* @since 2020/4/21
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class InstanceLogContent implements PowerSerializable {
/**
* Id of instance.
*/
private long instanceId;
/**
* Submitted time of the log.
*/
private long logTime;
/**
* Level of the log.
*/
private int logLevel;
/**
* Content of the log.
*/
private String logContent;
}

View File

@ -0,0 +1,23 @@
package tech.powerjob.common.model;
import lombok.Data;
import tech.powerjob.common.PowerSerializable;
/**
* 调度元信息
*
* @author tjq
* @since 2025/8/17
*/
@Data
public class InstanceMeta implements PowerSerializable {
/**
* expectTriggerTime
* 原始的期望调度时间无论重试N次都保留最开始的期望调度时间
*/
private Long ett;
public InstanceMeta() {
}
}

View File

@ -0,0 +1,25 @@
package tech.powerjob.common.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
/**
* 任务运行时高级配置
*
* @author tjq
* @since 2024/2/24
*/
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class JobAdvancedRuntimeConfig {
/**
* MR 任务专享参数TaskTracker 行为 {@link tech.powerjob.common.enums.TaskTrackerBehavior}
*/
private Integer taskTrackerBehavior;
}

View File

@ -0,0 +1,28 @@
package tech.powerjob.common.model;
import lombok.Data;
import tech.powerjob.common.serialize.JsonUtils;
/**
* @author Echo009
* @since 2022/3/22
*/
@Data
public class LifeCycle {
public static final LifeCycle EMPTY_LIFE_CYCLE = new LifeCycle();
private Long start;
private Long end;
public static LifeCycle parse(String lifeCycle){
try {
return JsonUtils.parseObject(lifeCycle,LifeCycle.class);
}catch (Exception e){
// ignore
return EMPTY_LIFE_CYCLE;
}
}
}

View File

@ -0,0 +1,32 @@
package tech.powerjob.common.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
/**
* 任务日志配置
*
* @author yhz
* @since 2022/9/16
*/
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class LogConfig {
/**
* log type {@link tech.powerjob.common.enums.LogType}
*/
private Integer type;
/**
* log level {@link tech.powerjob.common.enums.LogLevel}
*/
private Integer level;
private String loggerName;
}

View File

@ -0,0 +1,140 @@
package tech.powerjob.common.model;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import tech.powerjob.common.enums.WorkflowNodeType;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.List;
/**
* Points & edges for DAG, making it easier to describe or transfer.
*
* @author tjq
* @since 2020/5/26
*/
@Data
@NoArgsConstructor
public class PEWorkflowDAG implements Serializable {
/**
* Nodes of DAG diagram.
*/
private List<Node> nodes;
/**
* Edges of DAG diagram.
*/
private List<Edge> edges;
/**
* Point.
*/
@Data
@Accessors(chain = true)
@NoArgsConstructor
public static class Node implements Serializable {
/**
* node id
*
* @since 20210128
*/
private Long nodeId;
/* Instance running param, which is not required by DAG. */
/**
* note type
*
* @see WorkflowNodeType
* @since 20210316
*/
private Integer nodeType;
/**
* job id or workflow id (if this Node type is a nested workflow)
*
* @see WorkflowNodeType#NESTED_WORKFLOW
*/
private Long jobId;
/**
* node name
*/
private String nodeName;
@JsonSerialize(using = ToStringSerializer.class)
private Long instanceId;
/**
* for decision node, it is JavaScript code
*/
private String nodeParams;
private Integer status;
/**
* for decision node, it only be can "true" or "false"
*/
private String result;
/**
* instanceId will be null if disable .
*/
private Boolean enable;
/**
* mark node which disable by control node.
*/
private Boolean disableByControlNode;
private Boolean skipWhenFailed;
private String startTime;
private String finishedTime;
public Node(Long nodeId) {
this.nodeId = nodeId;
this.nodeType = WorkflowNodeType.JOB.getCode();
}
public Node(Long nodeId, Integer nodeType) {
this.nodeId = nodeId;
this.nodeType = nodeType;
}
}
/**
* Edge formed by two node ids.
*/
@Data
@NoArgsConstructor
public static class Edge implements Serializable {
private Long from;
private Long to;
/**
* property,support for complex flow control
* for decision node , it can be "true" or "false"
*/
private String property;
private Boolean enable;
public Edge(long from, long to) {
this.from = from;
this.to = to;
}
public Edge(long from, long to, String property) {
this.from = from;
this.to = to;
this.property = property;
}
}
public PEWorkflowDAG(@Nonnull List<Node> nodes, @Nullable List<Edge> edges) {
this.nodes = nodes;
this.edges = edges == null ? Lists.newLinkedList() : edges;
}
}

View File

@ -0,0 +1,118 @@
package tech.powerjob.common.model;
import tech.powerjob.common.PowerSerializable;
import lombok.Data;
/**
* Class for system metrics.
*
* @author tjq
* @since 2020/3/25
*/
@Data
public class SystemMetrics implements PowerSerializable, Comparable<SystemMetrics> {
/**
* CPU processor num.
*/
private int cpuProcessors;
/**
* Percent of CPU load.
*/
private double cpuLoad;
/**
* Memory that is used by JVM, in GB.
*/
private double jvmUsedMemory;
/**
* Max memory that JVM can use, in GB.
*/
private double jvmMaxMemory;
/**
* Ratio of memory that JVM uses to total memory, 0.X,
* the value is between 0 and 1.
*/
private double jvmMemoryUsage;
/**
* Total used disk space, in GB.
*/
private double diskUsed;
/**
* Total disk space, in GB.
*/
private double diskTotal;
/**
* Used disk ratio.
*/
private double diskUsage;
/**
* user-customized system metrics collector, eg. GPU usage
* implement SystemMetricsCollector to set the value in worker side
* implement WorkerFilter to filter the worker in server side
*/
private String extra;
/**
* Score of cache.
*/
private int score;
/**
* Override compareTo.
*
* @param that the metrics that is to be compared with current.
* @return {@code int}
*/
@Override
public int compareTo(SystemMetrics that) {
// Sort by metrics in descending order.
return that.calculateScore() - this.calculateScore();
}
/**
* Calculate score, based on CPU and memory info.
*
* @return score
*/
public int calculateScore() {
if (score > 0) {
return score;
}
// Memory is vital to TaskTracker, so we set the multiplier factor as 2.
double memScore = (jvmMaxMemory - jvmUsedMemory) * 2;
// Calculate the remaining load of CPU. Multiplier is set as 1.
double cpuScore = cpuProcessors - cpuLoad;
// Windows can not fetch CPU load, set cpuScore as 1.
if (cpuScore > cpuProcessors) {
cpuScore = 1;
}
score = (int) (memScore + cpuScore);
return score;
}
/**
* Judge if the machine is available.
*
* @param minCPUCores Minimum available CPU cores.
* @param minMemorySpace Minimum available memory size
* @param minDiskSpace Minimum disk space
* @return {@code boolean} whether the machine is available.
*/
public boolean available(double minCPUCores, double minMemorySpace, double minDiskSpace) {
double availableMemory = jvmMaxMemory - jvmUsedMemory;
double availableDisk = diskTotal - diskUsed;
if (availableMemory < minMemorySpace || availableDisk < minDiskSpace) {
return false;
}
// 0 indicates the CPU is free, which is the optimal condition.
// Negative number means being unable to fetch CPU info, return true.
if (cpuLoad <= 0 || minCPUCores <= 0) {
return true;
}
return minCPUCores < (cpuProcessors - cpuLoad);
}
}

View File

@ -0,0 +1,43 @@
package tech.powerjob.common.model;
import lombok.Data;
import lombok.experimental.Accessors;
import tech.powerjob.common.PowerSerializable;
/**
* Task 详细信息
*
* @author tjq
* @since 2024/2/25
*/
@Data
@Accessors(chain = true)
public class TaskDetailInfo implements PowerSerializable {
private String taskId;
private String taskName;
/**
* 任务对象map 的 subTask
*/
private String taskContent;
/**
* 处理器地址
*/
private String processorAddress;
private Integer status;
private String statusStr;
private String result;
private Integer failedCnt;
/**
* 创建时间
*/
private Long createdTime;
/**
* 最后修改时间
*/
private Long lastModifiedTime;
/**
* ProcessorTracker 最后上报时间
*/
private Long lastReportTime;
}

View File

@ -0,0 +1,24 @@
package tech.powerjob.common.model;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* WorkerAppInfo
*
* @author tjq
* @since 2023/9/2
*/
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class WorkerAppInfo implements Serializable {
/**
* 应用唯一 ID
*/
private Long appId;
}

View File

@ -0,0 +1,35 @@
package tech.powerjob.common.request;
import tech.powerjob.common.PowerSerializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Worker部署Container请求
*
* @author tjq
* @since 2020/5/16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ServerDeployContainerRequest implements PowerSerializable {
/**
* 容器ID
*/
private Long containerId;
/**
* 容器名称
*/
private String containerName;
/**
* 文件名MD5值用于做版本校验和文件下载
*/
private String version;
/**
* 下载地址
*/
private String downloadURL;
}

View File

@ -0,0 +1,19 @@
package tech.powerjob.common.request;
import tech.powerjob.common.PowerSerializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 服务器销毁容器请求
*
* @author tjq
* @since 2020/5/18
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ServerDestroyContainerRequest implements PowerSerializable {
private Long containerId;
}

View File

@ -0,0 +1,62 @@
package tech.powerjob.common.request;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import tech.powerjob.common.enums.Protocol;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* 服务发现请求
*
* @author tjq
* @since 2023/1/21
*/
@Setter
@Accessors(chain = true)
public class ServerDiscoveryRequest implements Serializable {
private Long appId;
private String protocol;
private String currentServer;
private String clientVersion;
public Map<String, Object> toMap() {
Map<String, Object> ret = new HashMap<>();
// testMode 下 appId 可能为空,此处不判断会导致 testMode 无法启动 #580
if (appId != null) {
ret.put("appId", appId);
}
ret.put("protocol", protocol);
if (StringUtils.isNotEmpty(currentServer)) {
ret.put("currentServer", currentServer);
}
if (StringUtils.isNotEmpty(clientVersion)) {
ret.put("clientVersion", clientVersion);
}
return ret;
}
public Long getAppId() {
return appId;
}
public String getProtocol() {
return Optional.ofNullable(protocol).orElse(Protocol.AKKA.name());
}
public String getCurrentServer() {
return currentServer;
}
public String getClientVersion() {
return clientVersion;
}
}

View File

@ -0,0 +1,27 @@
package tech.powerjob.common.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import tech.powerjob.common.PowerSerializable;
/**
* 服务器查询实例运行状态,需要返回详细的运行数据
*
* @author tjq
* @since 2020/4/10
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ServerQueryInstanceStatusReq implements PowerSerializable {
private Long instanceId;
/**
* 自定义查询
* 针对高阶用户,直接开放底库查询,便于运维和排查问题
* 此处只传递查询条件,前置拼接 select *,后置拼接 limit
*/
private String customQuery;
}

View File

@ -0,0 +1,110 @@
package tech.powerjob.common.request;
import lombok.Data;
import tech.powerjob.common.PowerSerializable;
import java.util.List;
/**
* 服务端调度任务请求(一次任务处理的入口)
*
* @author tjq
* @since 2020/3/17
*/
@Data
public class ServerScheduleJobReq implements PowerSerializable {
/**
* 可用处理器地址,可能多值,逗号分隔
*/
private List<String> allWorkerAddress;
/**
* 最大机器数量
*/
private Integer maxWorkerCount;
/* *********************** 任务相关属性 *********************** */
/**
* 任务ID当更换Server后需要根据 JobId 重新查询任务元数据
*/
private Long jobId;
private Long wfInstanceId;
/**
* 基础信息
*/
private Long instanceId;
/**
* 任务执行类型单机、广播、MR
*/
private String executeType;
/**
* 处理器类型(内建,外部)
*/
private String processorType;
/**
* 处理器信息
*/
private String processorInfo;
/**
* 整个任务的总体超时时间
*/
private long instanceTimeoutMS;
/**
* 任务级别的参数相当于类的static变量
*/
private String jobParams;
/**
* 实例级别的参数相当于类的普通变量API触发专用从API触发处带入
*/
private String instanceParams;
/**
* 每台机器的处理线程数上限
*/
private int threadConcurrency;
/**
* 子任务重试次数任务本身的重试机制由server控制
*/
private int taskRetryNum;
/**
* 时间表达式类型CRON/API/FIX_RATE/FIX_DELAY
*/
private String timeExpressionType;
/**
* 时间表达式CRON/NULL/LONG/LONG单位MS
*/
private String timeExpression;
/**
* 最大同时运行任务数,默认 1
*/
private Integer maxInstanceNum;
/**
* 告警配置
*/
private String alarmConfig;
/**
* 日志配置
*/
private String logConfig;
/**
* 高级运行时配置
*/
private String advancedRuntimeConfig;
/**
* 调度元信息
*/
private String meta;
}

View File

@ -0,0 +1,20 @@
package tech.powerjob.common.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import tech.powerjob.common.PowerSerializable;
/**
* 服务器要求任务实例停止执行请求
*
* @author tjq
* @since 2020/4/9
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ServerStopInstanceReq implements PowerSerializable {
private Long instanceId;
}

View File

@ -0,0 +1,63 @@
package tech.powerjob.common.request;
import tech.powerjob.common.PowerSerializable;
import lombok.Data;
import java.util.Map;
/**
* TaskTracker 将状态上报给服务器
*
* @author tjq
* @since 2020/3/17
*/
@Data
public class TaskTrackerReportInstanceStatusReq implements PowerSerializable {
/**
* 追加上报自己的 appId
* 方便后续的监控日志埋点
*/
private Long appId;
private Long jobId;
private Long instanceId;
private Long wfInstanceId;
/**
* 追加的工作流上下文数据
* @since 2021/02/05
*/
private Map<String,String> appendedWfContext;
private int instanceStatus;
private String result;
/* ********* 统计信息 ********* */
private long totalTaskNum;
private long succeedTaskNum;
private long failedTaskNum;
private long startTime;
private Long endTime;
private long reportTime;
private String sourceAddress;
/* ********* 秒级任务的告警信息 ********* */
private boolean needAlert;
private String alertContent;
}

View File

@ -0,0 +1,71 @@
package tech.powerjob.common.request;
import tech.powerjob.common.PowerSerializable;
import tech.powerjob.common.model.DeployedContainerInfo;
import tech.powerjob.common.model.SystemMetrics;
import lombok.Data;
import java.util.List;
/**
* Worker 上报健康信息worker定时发送的heartbeat
*
* @author tjq
* @since 2020/3/25
*/
@Data
public class WorkerHeartbeat implements PowerSerializable {
/**
* 本机地址 -> IP:port
*/
private String workerAddress;
/**
* 当前 appName
*/
private String appName;
/**
* 当前 appId
*/
private Long appId;
/**
* 当前时间
*/
private long heartbeatTime;
/**
* 当前加载的容器(容器名称 -> 容器版本)
*/
private List<DeployedContainerInfo> containerInfos;
/**
* worker 版本信息
*/
private String version;
/**
* 使用的通讯协议 AKKA / HTTP
*/
private String protocol;
/**
* worker tag标识同一个 worker 下的一类集群 ISSUE: 226
*/
private String tag;
/**
* 客户端名称
*/
private String client;
/**
* 扩展字段
*/
private String extra;
/**
* 是否已经超载,超载的情况下 Server 一段时间内不会再向其派发任务
*/
private boolean isOverload;
private int lightTaskTrackerNum;
private int heavyTaskTrackerNum;
private SystemMetrics systemMetrics;
}

View File

@ -0,0 +1,23 @@
package tech.powerjob.common.request;
import tech.powerjob.common.PowerSerializable;
import tech.powerjob.common.model.InstanceLogContent;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 日志上报请求
*
* @author tjq
* @since 2020/4/23
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WorkerLogReportReq implements PowerSerializable {
private String workerAddress;
private List<InstanceLogContent> instanceLogContents;
}

View File

@ -0,0 +1,19 @@
package tech.powerjob.common.request;
import tech.powerjob.common.PowerSerializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Worker需要部署容器主动向Server请求信息
*
* @author tjq
* @since 2020/5/16
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WorkerNeedDeployContainerRequest implements PowerSerializable {
private Long containerId;
}

View File

@ -0,0 +1,20 @@
package tech.powerjob.common.request;
import tech.powerjob.common.PowerSerializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* worker 查询 执行器集群(动态上线需要)
*
* @author tjq
* @since 10/17/20
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WorkerQueryExecutorClusterReq implements PowerSerializable {
private Long appId;
private Long jobId;
}

View File

@ -0,0 +1,37 @@
package tech.powerjob.common.request.http;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 运行任务
*
* @author tjq
* @since 2025/8/16
*/
@Data
@Accessors(chain = true)
public class RunJobRequest {
private Long jobId;
/**
* 运行时参数
*/
private String instanceParams;
/**
* 延迟执行的时间,单位毫秒
*/
private Long delay;
/**
* “外键”,用于 OPENAPI 场景业务场景与 PowerJob 实例的绑定
*/
private String outerKey;
/**
* 扩展属性,用于 OPENAPI 场景上下文参数的透传
*/
private String extendValue;
/* 无需填写,系统自动填充 */
private Long appId;
}

View File

@ -0,0 +1,182 @@
package tech.powerjob.common.request.http;
import tech.powerjob.common.enums.DispatchStrategy;
import tech.powerjob.common.enums.ExecuteType;
import tech.powerjob.common.enums.ProcessorType;
import tech.powerjob.common.enums.TimeExpressionType;
import tech.powerjob.common.model.AlarmConfig;
import tech.powerjob.common.model.JobAdvancedRuntimeConfig;
import tech.powerjob.common.model.LogConfig;
import tech.powerjob.common.model.LifeCycle;
import tech.powerjob.common.utils.CommonUtils;
import lombok.Data;
import tech.powerjob.common.response.JobInfoDTO;
import java.util.List;
/**
* Save or modify {@link JobInfoDTO}
*
* @author tjq
* @since 2020/3/30
*/
@Data
public class SaveJobInfoRequest {
/**
* id of the job. set null to create or non-null to update the job.
*/
private Long id;
/* ************************** Base info of related job. ************************** */
/**
* Name of the job.
*/
private String jobName;
/**
* Description of the job.
*/
private String jobDescription;
/**
* Related id of the application. There is not need to set this property
* in PowerJob-client, as it would be set automatically.
*/
private Long appId;
/**
* Params that these jobs carry with when they are created.
*/
private String jobParams;
/* ************************** Timing param. ************************** */
/**
* Time expression type.
*/
private TimeExpressionType timeExpressionType;
/**
* Time expression.
*/
private String timeExpression;
/* ************************** Execution type. ************************** */
/**
* Execution type, {@code standalone}, {@code broadcast} or {@code Map/MapReduce}
*/
private ExecuteType executeType;
/**
* Processor type, {@code Java}, {@code Python} or {@code Shell}.
*/
private ProcessorType processorType;
/**
* Processor info.
*/
private String processorInfo;
/* ************************** Running instance setting. ************************** */
/**
* Maximum instance setting num. {@code 0} means there is no restriction.
*/
private Integer maxInstanceNum = 0;
/**
* Concurrency setting. Number of threads that run simultaneously.
*/
private Integer concurrency = 5;
/**
* Max instance running time setting. {@code 0L} means there is no restriction.
*/
private Long instanceTimeLimit = 0L;
/* ************************** Retrial setting. ************************** */
/**
* Instance retry number setting.
*/
private Integer instanceRetryNum = 0;
/**
* Task retry number setting.
*/
private Integer taskRetryNum = 0;
/* ************************** Busy Machine setting. ************************** */
/**
* Minimum CPU required. {@code 0} means there is no restriction.
*/
private double minCpuCores = 0;
/**
* Minimum memory required, in GB.
*/
private double minMemorySpace = 0;
/**
* Minimum disk space, in GB. {@code 0} means there is no restriction.
*/
private double minDiskSpace = 0;
private boolean enable = true;
/* ************************** PowerJob-worker cluster property ************************** */
/**
* Designated PowerJob-worker nodes. Blank value indicates that there is
* no limit. Non-blank value means to run the corresponding machine(s) only.
* example: 192.168.1.1:27777,192.168.1.2:27777
*/
private String designatedWorkers;
/**
* Max count of PowerJob-worker nodes.
*/
private Integer maxWorkerCount = 0;
/**
* The id list of the users that need to be notified.
*/
private List<Long> notifyUserIds;
private String extra;
private DispatchStrategy dispatchStrategy;
/**
* 某种派发策略背后的具体配置,值取决于 dispatchStrategy
*/
private String dispatchStrategyConfig;
private LifeCycle lifeCycle;
/**
* alarm config
*/
private AlarmConfig alarmConfig;
/**
* 任务归类,开放给接入方自由定制
*/
private String tag;
/**
* 日志配置,包括日志级别、日志方式等配置信息
*/
private LogConfig logConfig;
/**
* 高级运行时配置
*/
private JobAdvancedRuntimeConfig advancedRuntimeConfig;
/**
* Check non-null properties.
*/
public void valid() {
CommonUtils.requireNonNull(jobName, "jobName can't be empty");
CommonUtils.requireNonNull(appId, "appId can't be empty");
CommonUtils.requireNonNull(processorInfo, "processorInfo can't be empty");
CommonUtils.requireNonNull(executeType, "executeType can't be empty");
CommonUtils.requireNonNull(processorType, "processorType can't be empty");
CommonUtils.requireNonNull(timeExpressionType, "timeExpressionType can't be empty");
}
public DispatchStrategy getDispatchStrategy() {
if (dispatchStrategy == null) {
return DispatchStrategy.HEALTH_FIRST;
}
return dispatchStrategy;
}
}

View File

@ -0,0 +1,55 @@
package tech.powerjob.common.request.http;
import lombok.Data;
import tech.powerjob.common.enums.WorkflowNodeType;
import tech.powerjob.common.utils.CommonUtils;
/**
* 保存工作流节点信息请求
* 工作流节点的
*
* @author zenggonggu
* @since 2021/02/02
*/
@Data
public class SaveWorkflowNodeRequest {
private Long id;
private Long appId;
/**
* 节点类型(默认为任务节点)
*/
private Integer type;
/**
* 任务 ID
*/
private Long jobId;
/**
* 节点别名,默认为对应的任务名称
*/
private String nodeName;
/**
* 节点参数
*/
private String nodeParams;
/**
* 是否启用,默认启用
*/
private Boolean enable = true;
/**
* 是否允许失败跳过,默认不允许
*/
private Boolean skipWhenFailed = false;
public void valid() {
CommonUtils.requireNonNull(this.appId, "appId can't be empty");
CommonUtils.requireNonNull(this.type, "type can't be empty");
final WorkflowNodeType workflowNodeType = WorkflowNodeType.of(type);
if (workflowNodeType == WorkflowNodeType.JOB || workflowNodeType == WorkflowNodeType.NESTED_WORKFLOW) {
CommonUtils.requireNonNull(this.jobId, "jobId can't be empty");
}
}
}

View File

@ -0,0 +1,74 @@
package tech.powerjob.common.request.http;
import tech.powerjob.common.enums.TimeExpressionType;
import tech.powerjob.common.model.LifeCycle;
import tech.powerjob.common.model.PEWorkflowDAG;
import tech.powerjob.common.utils.CommonUtils;
import com.google.common.collect.Lists;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 创建/修改 Workflow 请求
*
* @author tjq
* @since 2020/5/26
*/
@Data
public class SaveWorkflowRequest implements Serializable {
private Long id;
/**
* 工作流名称
*/
private String wfName;
/**
* 工作流描述
*/
private String wfDescription;
/**
* 所属应用IDOpenClient不需要用户填写自动填充
*/
private Long appId;
/* ************************** 定时参数 ************************** */
/**
* 时间表达式类型,仅支持 CRON 和 API
*/
private TimeExpressionType timeExpressionType;
/**
* 时间表达式CRON/NULL/LONG/LONG
*/
private String timeExpression;
/**
* 最大同时运行的工作流个数,默认 1
*/
private Integer maxWfInstanceNum = 1;
/**
* ENABLE / DISABLE
*/
private boolean enable = true;
/**
* 工作流整体失败的报警
*/
private List<Long> notifyUserIds = Lists.newLinkedList();
/** 点线表示法*/
private PEWorkflowDAG dag;
private LifeCycle lifeCycle;
public void valid() {
CommonUtils.requireNonNull(wfName, "workflow name can't be empty");
CommonUtils.requireNonNull(appId, "appId can't be empty");
CommonUtils.requireNonNull(timeExpressionType, "timeExpressionType can't be empty");
}
}

View File

@ -0,0 +1,38 @@
package tech.powerjob.common.request.query;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.List;
/**
* 任务实例分页查询
*
* @author tjq
* @since 2024/11/21
*/
@Getter
@Setter
public class InstancePageQuery extends PowerPageQuery {
private Long instanceIdEq;
private Long instanceIdLt;
private Long instanceIdGt;
/**
* 根据业务外建查询
*/
private String outerKeyEq;
private Long jobIdEq;
private List<Integer> statusIn;
private Date gmtCreateLt;
private Date gmtCreateGt;
private Date gmtModifiedLt;
private Date gmtModifiedGt;
}

View File

@ -0,0 +1,55 @@
package tech.powerjob.common.request.query;
import tech.powerjob.common.PowerQuery;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.List;
/**
* Query JobInfo
*
* @author tjq
* @since 1/16/21
*/
@Getter
@Setter
@Accessors(chain = true)
public class JobInfoQuery extends PowerQuery {
private Long idEq;
private Long idLt;
private Long idGt;
private String jobNameEq;
private String jobNameLike;
private String jobDescriptionLike;
private String jobParamsLike;
private List<Integer> timeExpressionTypeIn;
private List<Integer> executeTypeIn;
private List<Integer> processorTypeIn;
private String processorInfoEq;
private String processorInfoLike;
private List<Integer> statusIn;
private Long nextTriggerTimeGt;
private Long nextTriggerTimeLt;
private String notifyUserIdsLike;
private Date gmtCreateLt;
private Date gmtCreateGt;
private Date gmtModifiedLt;
private Date gmtModifiedGt;
private Integer dispatchStrategyEq;
private String tagEq;
}

View File

@ -0,0 +1,41 @@
package tech.powerjob.common.request.query;
import lombok.Getter;
import lombok.Setter;
import tech.powerjob.common.PowerQuery;
import java.io.Serializable;
/**
* 分页查询
*
* @author tjq
* @since 2024/11/21
*/
@Getter
@Setter
public class PowerPageQuery extends PowerQuery implements Serializable {
/* ****************** 分页参数 ****************** */
/**
* 当前页码
*/
protected Integer index = 0;
/**
* 页大小
*/
protected Integer pageSize = 10;
/* ****************** 排序参数 ****************** */
/**
* 排序参数,如 gmtCreate、instanceId
*/
protected String sortBy;
/**
* asc是指定列按升序排列desc则是指定列按降序排列
*/
protected boolean asc = false;
}

View File

@ -0,0 +1,61 @@
package tech.powerjob.common.response;
import tech.powerjob.common.PowerSerializable;
import tech.powerjob.common.serialize.JsonUtils;
import lombok.*;
import java.nio.charset.StandardCharsets;
/**
* Pattens.ask 的响应
*
* @author tjq
* @since 2020/3/18
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AskResponse implements PowerSerializable {
private boolean success;
/*
- 使用 Object 会报错java.lang.ClassCastException: scala.collection.immutable.HashMap cannot be cast to XXX只能自己序列化反序列化了
- 嵌套类型(比如 Map<String, B>如果B也是个复杂对象那么反序列化后B的类型为 LinkedHashMap... 处理比较麻烦转成JSON再转回来
- 考虑到多语言通讯data 必须使用 JSON 序列化为字节数组
*/
private byte[] data;
// 错误信息
private String message;
public static AskResponse succeed(Object data) {
AskResponse r = new AskResponse();
r.success = true;
if (data != null) {
if (data instanceof String) {
r.data = ((String) data).getBytes(StandardCharsets.UTF_8);
} else {
r.data = JsonUtils.toBytes(data);
}
}
return r;
}
public static AskResponse failed(String msg) {
AskResponse r = new AskResponse();
r.success = false;
r.message = msg;
return r;
}
public <T> T getData(Class<T> clz) throws Exception {
return JsonUtils.parseObject(data, clz);
}
public String parseDataAsString() {
return new String(data, StandardCharsets.UTF_8);
}
}

View File

@ -0,0 +1,86 @@
package tech.powerjob.common.response;
import tech.powerjob.common.enums.InstanceStatus;
import lombok.Data;
import java.util.Date;
/**
* instanceInfo Network transmission object
*
* @author tjq
* @since 2020/5/14
*/
@Data
public class InstanceInfoDTO {
/**
* 任务ID
*/
private Long jobId;
/**
* 任务所属应用的ID冗余提高查询效率
*/
private Long appId;
/**
* 任务实例ID
*/
private Long instanceId;
/**
* 工作流实例ID
*/
private Long wfInstanceId;
/**
* 任务参数
*/
private String jobParams;
/**
* 任务实例参数
*/
private String instanceParams;
/**
* 任务状态 {@link InstanceStatus}
*/
private int status;
/**
* 该任务实例的类型,普通/工作流InstanceType
*/
private Integer type;
/**
* 执行结果
*/
private String result;
/**
* 预计触发时间
*/
private Long expectedTriggerTime;
/**
* 实际触发时间
*/
private Long actualTriggerTime;
/**
* 结束时间
*/
private Long finishedTime;
/**
* TaskTracker地址
*/
private String taskTrackerAddress;
/**
* 总共执行的次数(用于重试判断)
*/
private Long runningTimes;
/**
* “外键”,用于 OPENAPI 场景业务场景与 PowerJob 实例的绑定
*/
private String outerKey;
/**
* 扩展属性,用于 OPENAPI 场景上下文参数的透传
*/
private String extendValue;
private Date gmtCreate;
private Date gmtModified;
}

View File

@ -0,0 +1,149 @@
package tech.powerjob.common.response;
import lombok.Data;
import tech.powerjob.common.model.AlarmConfig;
import tech.powerjob.common.model.JobAdvancedRuntimeConfig;
import tech.powerjob.common.model.LogConfig;
import java.util.Date;
/**
* jobInfo 对外输出对象
*
* @author tjq
* @since 2020/5/14
*/
@Data
public class JobInfoDTO {
private Long id;
/* ************************** 任务基本信息 ************************** */
/**
* 任务名称
*/
private String jobName;
/**
* 任务描述
*/
private String jobDescription;
/**
* 任务所属的应用ID
*/
private Long appId;
/**
* 任务自带的参数
*/
private String jobParams;
/* ************************** 定时参数 ************************** */
/**
* 时间表达式类型CRON/API/FIX_RATE/FIX_DELAY
*/
private Integer timeExpressionType;
/**
* 时间表达式CRON/NULL/LONG/LONG
*/
private String timeExpression;
/* ************************** 执行方式 ************************** */
/**
* 执行类型,单机/广播/MR
*/
private Integer executeType;
/**
* 执行器类型Java/Shell
*/
private Integer processorType;
/**
* 执行器信息
*/
private String processorInfo;
/* ************************** 运行时配置 ************************** */
/**
* 最大同时运行任务数,默认 1
*/
private Integer maxInstanceNum;
/**
* 并发度,同时执行某个任务的最大线程数量
*/
private Integer concurrency;
/**
* 任务整体超时时间
*/
private Long instanceTimeLimit;
/** ************************** 重试配置 ************************** */
private Integer instanceRetryNum;
private Integer taskRetryNum;
/**
* 1 正常运行2 停止(不再调度)
*/
private Integer status;
/**
* 下一次调度时间
*/
private Long nextTriggerTime;
/* ************************** 繁忙机器配置 ************************** */
/**
* 最低CPU核心数量0代表不限
*/
private double minCpuCores;
/**
* 最低内存空间,单位 GB0代表不限
*/
private double minMemorySpace;
/**
* 最低磁盘空间,单位 GB0代表不限
*/
private double minDiskSpace;
/* ************************** 集群配置 ************************** */
/**
* 指定机器运行,空代表不限,非空则只会使用其中的机器运行(多值逗号分割)
*/
private String designatedWorkers;
/**
* 最大机器数量
*/
private Integer maxWorkerCount;
/**
* 报警用户ID列表多值逗号分隔
*/
private String notifyUserIds;
private Date gmtCreate;
private Date gmtModified;
private String extra;
/**
* 派发策略
*/
private Integer dispatchStrategy;
/**
* 某种派发策略背后的具体配置,值取决于 dispatchStrategy
*/
private String dispatchStrategyConfig;
private String lifecycle;
private AlarmConfig alarmConfig;
/**
* 任务归类,开放给接入方自由定制
*/
private String tag;
/**
* 日志配置,包括日志级别、日志方式等配置信息
*/
private LogConfig logConfig;
private JobAdvancedRuntimeConfig advancedRuntimeConfig;
}

View File

@ -0,0 +1,10 @@
package tech.powerjob.common.response;
/**
* 主要目的:消除 idea 烦人的类型提示
*
* @author tjq
* @since 2023/9/2
*/
public class ObjectResultDTO extends ResultDTO<Object> {
}

View File

@ -0,0 +1,42 @@
package tech.powerjob.common.response;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* 分页对象
*
* @author tjq
* @since 2020/4/12
*/
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class PageResult<T> implements Serializable {
/**
* 当前页数
*/
private int index;
/**
* 页大小
*/
private int pageSize;
/**
* 总页数
*/
private int totalPages;
/**
* 总数据量
*/
private long totalItems;
/**
* 数据
*/
private List<T> data;
}

View File

@ -0,0 +1,47 @@
package tech.powerjob.common.response;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.exception.ExceptionUtils;
import tech.powerjob.common.enums.ErrorCodes;
import tech.powerjob.common.exception.PowerJobException;
/**
* 新的 Result带状态码
*
* @author 程序帕鲁
* @since 2024/2/19
*/
@Getter
@Setter
public class PowerResultDTO<T> extends ResultDTO<T> {
private String code;
public static <T> PowerResultDTO<T> s(T data) {
PowerResultDTO<T> r = new PowerResultDTO<>();
r.success = true;
r.data = data;
return r;
}
public static <T> PowerResultDTO<T> f(String message) {
PowerResultDTO<T> r = new PowerResultDTO<>();
r.success = false;
r.message = message;
return r;
}
public static <T> PowerResultDTO<T> f(Throwable t) {
PowerResultDTO<T> f = f(ExceptionUtils.getStackTrace(t));
f.setCode(ErrorCodes.SYSTEM_UNKNOWN_ERROR.getCode());
return f;
}
public static <T> PowerResultDTO<T> f(PowerJobException pje) {
PowerResultDTO<T> f = f(pje.getMessage());
f.setCode(pje.getCode());
return f;
}
}

View File

@ -0,0 +1,47 @@
package tech.powerjob.common.response;
import tech.powerjob.common.PowerSerializable;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.apache.commons.lang3.exception.ExceptionUtils;
/**
* The result object returned by the request
* <p>
* 低版本由于 Jackson 序列化配置问题,导致无法在此对象上新增任何字段了,否则会报错 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "code" (class tech.powerjob.common.response.ObjectResultDTO), not marked as ignorable (3 known properties: "data", "success", "message"])
* at [Source: (String)"{"success":true,"code":null,"data":2,"message":null}"; line: 1, column: 28] (through reference chain: tech.powerjob.common.response.ObjectResultDTO["code"])
* <p>
* 短期内所有的新增字段需求,都通过新对象继承实现
*
* @author tjq
* @since 2020/3/30
*/
@Getter
@Setter
@ToString
public class ResultDTO<T> implements PowerSerializable {
protected boolean success;
protected T data;
protected String message;
public static <T> ResultDTO<T> success(T data) {
ResultDTO<T> r = new ResultDTO<>();
r.success = true;
r.data = data;
return r;
}
public static <T> ResultDTO<T> failed(String message) {
ResultDTO<T> r = new ResultDTO<>();
r.success = false;
r.message = message;
return r;
}
public static <T> ResultDTO<T> failed(Throwable t) {
return failed(ExceptionUtils.getStackTrace(t));
}
}

View File

@ -0,0 +1,73 @@
package tech.powerjob.common.response;
import tech.powerjob.common.model.PEWorkflowDAG;
import lombok.Data;
import java.util.Date;
/**
* workflowInfo 对外输出对象
*
* @author tjq
* @since 2020/6/2
*/
@Data
public class WorkflowInfoDTO {
private Long id;
private String wfName;
private String wfDescription;
/**
* 所属应用ID
*/
private Long appId;
/**
* 工作流的DAG图信息点线式DAG的json
*/
private PEWorkflowDAG pEWorkflowDAG;
/* ************************** 定时参数 ************************** */
/**
* 时间表达式类型CRON/API/FIX_RATE/FIX_DELAY
*/
private String timeExpressionType;
/**
* 时间表达式CRON/NULL/LONG/LONG
*/
private String timeExpression;
/**
* 最大同时运行的工作流个数,默认 1
*/
private Integer maxWfInstanceNum;
/**
* 1 正常运行2 停止(不再调度)
*/
private Integer status;
/**
* 下一次调度时间
*/
private Long nextTriggerTime;
/**
* 工作流整体失败的报警
*/
private String notifyUserIds;
private Date gmtCreate;
private Date gmtModified;
/**
* ENABLE / DISABLE
*
* status 字段的转义
*/
private Boolean enable;
}

View File

@ -0,0 +1,53 @@
package tech.powerjob.common.response;
import lombok.Data;
import java.util.Date;
/**
* WorkflowInstanceInfo 对外输出对象
*
* @author tjq
* @since 2020/6/2
*/
@Data
public class WorkflowInstanceInfoDTO {
private Long id;
private Long appId;
private Long wfInstanceId;
private Long workflowId;
/**
* workflow 状态WorkflowInstanceStatus
*/
private Integer status;
/**
* 工作流启动参数
*/
private String wfInitParams;
/**
* 工作流上下文
*/
private String wfContext;
private String dag;
private String result;
/**
* 预计触发时间
*/
private Long expectedTriggerTime;
/**
* 实际触发时间
*/
private Long actualTriggerTime;
/**
* 结束时间
*/
private Long finishedTime;
private Date gmtCreate;
private Date gmtModified;
}

View File

@ -0,0 +1,50 @@
package tech.powerjob.common.response;
import lombok.Data;
import java.util.Date;
/**
* WorkflowNodeInfo 对外输出对象
* @author Echo009
* @since 2021/2/20
*/
@Data
public class WorkflowNodeInfoDTO {
private Long id;
private Long appId;
private Long workflowId;
/**
* 任务 ID
*/
private Long jobId;
/**
* 节点别名,默认为对应的任务名称
*/
private String nodeAlias;
/**
* 节点参数
*/
private String nodeParams;
/**
* 是否启用
*/
private Boolean enable;
/**
* 是否允许失败跳过
*/
private Boolean skipWhenFailed;
/**
* 创建时间
*/
private Date gmtCreate;
/**
* 更新时间
*/
private Date gmtModified;
}

View File

@ -0,0 +1,147 @@
package tech.powerjob.common.serialize;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import tech.powerjob.common.exception.ImpossibleException;
import tech.powerjob.common.exception.PowerJobException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* JSON工具类
*
* @author tjq
* @since 2020/4/16
*/
@Slf4j
public class JsonUtils {
private static final JsonMapper JSON_MAPPER = JsonMapper.builder()
.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true)
.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
.configure(JsonParser.Feature.IGNORE_UNDEFINED, true)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.build();
static {
JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 非核心功能可降级,尽可能降低依赖冲突概率
try {
JSON_MAPPER.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
} catch (Exception e) {
log.warn("[JsonUtils] registerJavaTimeModule failed, PowerJob can't process Java 8 date/time type now!", e);
}
}
private static final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<Map<String, Object>> () {};
private JsonUtils(){
}
public static String toJSONString(Object obj) {
if (obj == null) {
return null;
}
if (obj instanceof String) {
return (String) obj;
}
try {
return JSON_MAPPER.writeValueAsString(obj);
}catch (Exception e) {
log.error("[PowerJob] toJSONString failed", e);
}
return null;
}
public static String toJSONStringUnsafe(Object obj) {
if (obj instanceof String) {
return (String) obj;
}
try {
return JSON_MAPPER.writeValueAsString(obj);
}catch (Exception e) {
ExceptionUtils.rethrow(e);
}
throw new ImpossibleException();
}
public static byte[] toBytes(Object obj) {
try {
return JSON_MAPPER.writeValueAsBytes(obj);
}catch (Exception e) {
log.error("[PowerJob] serialize failed", e);
}
return null;
}
public static <T> T parseObject(String json, Class<T> clz) throws Exception {
return JSON_MAPPER.readValue(json, clz);
}
public static Map<String, Object> parseMap(String json) {
if (StringUtils.isEmpty(json)) {
return new HashMap<>();
}
try {
return JSON_MAPPER.readValue(json, MAP_TYPE_REFERENCE);
} catch (Exception e) {
ExceptionUtils.rethrow(e);
}
throw new ImpossibleException();
}
public static <T> T parseObject(byte[] b, Class<T> clz) throws IOException {
return JSON_MAPPER.readValue(b, clz);
}
public static <T> T parseObject(byte[] b, TypeReference<T> typeReference) throws IOException {
return JSON_MAPPER.readValue(b, typeReference);
}
public static <T> T parseObject(String json, TypeReference<T> typeReference) throws IOException {
return JSON_MAPPER.readValue(json, typeReference);
}
public static <T> T parseObjectIgnoreException(String json, Class<T> clz) {
if (StringUtils.isEmpty(json)) {
return null;
}
try {
return JSON_MAPPER.readValue(json, clz);
}catch (Exception e) {
log.error("unable to parse json string to object,current string:{}",json,e);
return null;
}
}
public static <T> T parseObjectUnsafe(String json, Class<T> clz) {
if (json == null) {
return null;
}
try {
return JSON_MAPPER.readValue(json, clz);
}catch (Exception e) {
ExceptionUtils.rethrow(e);
}
throw new PowerJobException("impossible");
}
public static <T> T toJavaObject(Object o, Class<T> clz) {
if (o == null) {
return null;
}
return JSON_MAPPER.convertValue(o, clz);
}
}

View File

@ -0,0 +1,52 @@
package tech.powerjob.common.serialize;
import com.esotericsoftware.kryo.kryo5.Kryo;
import com.esotericsoftware.kryo.kryo5.io.Input;
import com.esotericsoftware.kryo.kryo5.io.Output;
import com.esotericsoftware.kryo.kryo5.serializers.CompatibleFieldSerializer;
/**
* 序列化器
* V1.0.0对象池因无法解决反序列化容器类外部类的原因而被移除LastCommitId: a14f554e0085b6a179375a8ca04665434b73c7bd
* V1.2.0ThreadLocal + 手动设置Kryo所使用的类加载器默认类加载器为创建kryo的类对象Kryo.class的类加载器实现容器类的序列化和反序列化
*
* @author tjq
* @since 2020/3/25
*/
public class SerializerUtils {
//每个线程的 Kryo 实例
private static final ThreadLocal<Kryo> kryoLocal = ThreadLocal.withInitial(() -> {
Kryo kryo = new Kryo();
// 支持对象循环引用(否则会栈溢出),会导致性能些许下降 T_T
kryo.setReferences(true); //默认值就是 true添加此行的目的是为了提醒维护者不要改变这个配置
// 关闭序列化注册会导致性能些许下降但在分布式环境中注册类生成ID不一致会导致错误
kryo.setRegistrationRequired(false);
// 支持删除或者新增字段
kryo.setDefaultSerializer(CompatibleFieldSerializer.class);
// 设置类加载器为线程上下文类加载器如果Processor来源于容器必须使用容器的类加载器否则妥妥的CNF
kryo.setClassLoader(Thread.currentThread().getContextClassLoader());
return kryo;
});
public static byte[] serialize(Object obj) {
Kryo kryo = kryoLocal.get();
// 使用 Output 对象池会导致序列化重复的错误getBuffer返回了Output对象的buffer引用
try (Output opt = new Output(1024, -1)) {
kryo.writeClassAndObject(opt, obj);
opt.flush();
return opt.getBuffer();
}
}
public static Object deSerialized(byte[] buffer) {
Kryo kryo = kryoLocal.get();
return kryo.readClassAndObject(new Input(buffer));
}
}

View File

@ -0,0 +1,33 @@
package tech.powerjob.common.utils;
import java.util.Collection;
import java.util.Map;
/**
* CollectionUtils
*
* @author tjq
* @since 2023/1/20
*/
public class CollectionUtils {
/**
* Return {@code true} if the supplied Collection is {@code null} or empty.
* Otherwise, return {@code false}.
* @param collection the Collection to check
* @return whether the given Collection is empty
*/
public static boolean isEmpty(Collection<?> collection) {
return (collection == null || collection.isEmpty());
}
/**
* Return {@code true} if the supplied Map is {@code null} or empty.
* Otherwise, return {@code false}.
* @param map the Map to check
* @return whether the given Map is empty
*/
public static boolean isEmpty(Map<?, ?> map) {
return (map == null || map.isEmpty());
}
}

View File

@ -0,0 +1,195 @@
package tech.powerjob.common.utils;
import tech.powerjob.common.OmsConstant;
import tech.powerjob.common.exception.PowerJobException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
/**
* 公共工具类
*
* @author tjq
* @since 2020/3/18
*/
@Slf4j
public class CommonUtils {
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 重试执行,仅适用于失败抛出异常的方法
* @param executor 需要执行的方法
* @param tryTimes 尝试次数(总执行次数)
* @param intervalMS 失败后下一次执行的间隔时间
* @param <T> 执行函数返回值类型
* @return 函数成功执行后的返回值
* @throws Exception 执行失败,调用方自行处理
*/
public static <T> T executeWithRetry(SupplierPlus<T> executor, int tryTimes, long intervalMS) throws Exception {
if (tryTimes <= 1 || intervalMS <= 0) {
return executor.get();
}
for (int i = 1; i < tryTimes; i++) {
try {
return executor.get();
}catch (Exception e) {
Thread.sleep(intervalMS);
}
}
return executor.get();
}
public static <T> T executeWithRetry0(SupplierPlus<T> executor) throws Exception {
return executeWithRetry(executor, 3, 100);
}
/**
* 重试执行,仅适用于根据返回值决定是否执行成功的方法
* @param booleanExecutor 需要执行的方法,其返回值决定了执行是否成功
* @param tryTimes 尝试执行次数
* @param intervalMS 失败后下一次执行的间隔时间
* @return 最终执行结果
*/
public static boolean executeWithRetryV2(Supplier<Boolean> booleanExecutor, int tryTimes, long intervalMS) {
if (tryTimes <= 1 || intervalMS <= 0) {
return booleanExecutor.get();
}
for (int i = 1; i < tryTimes; i++) {
try {
if (booleanExecutor.get()) {
return true;
}
Thread.sleep(intervalMS);
}catch (Exception ignore) {
}
}
return booleanExecutor.get();
}
/**
* 生成数据库查询语句 in 后的条件
* @param collection eg,["a", "b", "c"]
* @return eg,('a','b','c')
*/
public static String getInStringCondition(Collection<String> collection) {
if (collection == null || collection.isEmpty()) {
return "()";
}
StringBuilder sb = new StringBuilder(" ( ");
collection.forEach(str -> sb.append("'").append(str).append("',"));
return sb.replace(sb.length() -1, sb.length(), " ) ").toString();
}
public static void executeIgnoreException(SupplierPlus<?> executor) {
try {
executor.get();
}catch (Exception ignore) {
}
}
public static void executeIgnoreException(Meaningless executor) {
try {
executor.m();
}catch (Exception ignore) {
}
}
/**
* 将大小格式化为 2的N次
* @param cap 初始大小
* @return 格式化后的大小2的N次
*/
public static int formatSize(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
public static <T> T requireNonNull(T obj, String msg) {
if (obj == null) {
throw new PowerJobException(msg);
}
if (obj instanceof String) {
if (StringUtils.isEmpty((String) obj)) {
throw new PowerJobException(msg);
}
}
if (obj instanceof Collection) {
if (CollectionUtils.isEmpty((Collection<?>) obj)) {
throw new PowerJobException(msg);
}
}
if (obj instanceof Map) {
if (MapUtils.isEmpty((Map<?, ?>) obj)) {
throw new PowerJobException(msg);
}
}
return obj;
}
/**
* 格式化时间,将时间戳转化为可阅读时间
* @param ts 时间戳
* @return 可阅读时间
*/
public static String formatTime(Long ts) {
if (ts == null || ts <= 0) {
return OmsConstant.NONE;
}
try {
return DateFormatUtils.format(ts, OmsConstant.TIME_PATTERN);
}catch (Exception ignore) {
}
return OmsConstant.NONE;
}
public static String formatTime(Date date) {
if (date == null) {
return OmsConstant.NONE;
}
return formatTime(date.getTime());
}
/**
* 格式化字符串,如果是 null 或空则显示 N/A
* @param str 字符串
* @return 结果
*/
public static String formatString(String str) {
if (StringUtils.isEmpty(str)) {
return OmsConstant.NONE;
}
return str;
}
/**
* 生成 UUID
* @return uuid
*/
public static String genUUID() {
return StringUtils.replace(UUID.randomUUID().toString(), "-", "");
}
public static void easySleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ignore) {
}
}
}

View File

@ -0,0 +1,47 @@
package tech.powerjob.common.utils;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import java.math.BigInteger;
import java.security.MessageDigest;
/**
* 加密工具
*
* @author tjq
* @since 2023/3/25
*/
public class DigestUtils {
/**
* 32位小写 md5
* @param input 输入
* @return md5
*/
@SneakyThrows
public static String md5(String input) {
if (StringUtils.isEmpty(input)) {
return null;
}
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(input.getBytes());
byte[] byteArray = md5.digest();
BigInteger bigInt = new BigInteger(1, byteArray);
// 参数16表示16进制
StringBuilder result = new StringBuilder(bigInt.toString(16));
// 不足32位高位补零
while(result.length() < 32) {
result.insert(0, "0");
}
return result.toString();
}
public static String rePassword(String password, String salt) {
String f1 = String.format("%s_%s_z", salt, password);
return String.format("%s_%s_b", salt, md5(f1));
}
}

View File

@ -0,0 +1,59 @@
package tech.powerjob.common.utils;
import tech.powerjob.common.exception.PowerJobException;
import okhttp3.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* 封装 OkHttpClient
*
* @author tjq
* @since 2020/4/6
*/
public class HttpUtils {
private static final OkHttpClient client;
private static final int HTTP_SUCCESS_CODE = 200;
static {
client = new OkHttpClient.Builder()
.connectTimeout(1, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
}
public static String get(String url) throws IOException {
Request request = new Request.Builder()
.get()
.url(url)
.build();
return execute(request);
}
public static String post(String url, RequestBody requestBody) throws IOException {
Request request = new Request.Builder()
.post(requestBody)
.url(url)
.build();
return execute(request);
}
private static String execute(Request request) throws IOException {
try (Response response = client.newCall(request).execute()) {
int responseCode = response.code();
if (responseCode == HTTP_SUCCESS_CODE) {
ResponseBody body = response.body();
if (body == null) {
return null;
}else {
return body.string();
}
}
throw new PowerJobException(String.format("http request failed,code=%d", responseCode));
}
}
}

View File

@ -0,0 +1,63 @@
package tech.powerjob.common.utils;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.CodeSource;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
/**
* Java 语言相关的工具
*
* @author tjq
* @since 2022/10/23
*/
@Slf4j
public class JavaUtils {
/**
* 获取类所在 Jar 包的版本
* @param clz 类
* @return 包版本
*/
public static String determinePackageVersion(Class<?> clz) {
try {
String implementationVersion = clz.getPackage().getImplementationVersion();
if (implementationVersion != null) {
return implementationVersion;
}
CodeSource codeSource = clz.getProtectionDomain().getCodeSource();
if (codeSource == null) {
return null;
}
URL codeSourceLocation = codeSource.getLocation();
URLConnection connection = codeSourceLocation.openConnection();
if (connection instanceof JarURLConnection) {
return getImplementationVersion(((JarURLConnection) connection).getJarFile());
}
final File file = new File(codeSourceLocation.toURI());
// idea 场景,查找版本失败
if (!file.exists() || file.isDirectory()) {
return "UNKNOWN";
}
try (JarFile jarFile = new JarFile(file)) {
return getImplementationVersion(jarFile);
}
}
catch (Throwable t) {
log.warn("[JavaUtils] determinePackageVersion for clz[{}] failed, msg: {}", clz.getSimpleName(), t.toString());
// windows 下无权限访问会一直报错一直重试,需要在此兼容
return "UNKNOWN";
}
}
private static String getImplementationVersion(JarFile jarFile) throws IOException {
return jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION);
}
}

View File

@ -0,0 +1,86 @@
package tech.powerjob.common.utils;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Map;
/**
* MapUtils
*
* @author tjq
* @since 2024/2/24
*/
public class MapUtils {
public static <K> String getString(Map<? super K, ?> map, K key) {
if (map != null) {
Object answer = map.get(key);
if (answer != null) {
return answer.toString();
}
}
return null;
}
public static <K> String getString(Map<? super K, ?> map, K key, String defaultValue) {
String answer = getString(map, key);
if (answer == null) {
answer = defaultValue;
}
return answer;
}
public static <K> Long getLong(Map<? super K, ?> map, K key, Long defaultValue) {
Long answer = getLong(map, key);
if (answer == null) {
answer = defaultValue;
}
return answer;
}
public static <K> long getLongValue(Map<? super K, ?> map, K key) {
Long longObject = getLong(map, key);
return longObject == null ? 0L : longObject;
}
public static <K> Long getLong(Map<? super K, ?> map, K key) {
Number answer = getNumber(map, key);
if (answer == null) {
return null;
} else {
return answer instanceof Long ? (Long)answer : answer.longValue();
}
}
public static <K> Number getNumber(Map<? super K, ?> map, K key) {
if (map != null) {
Object answer = map.get(key);
if (answer != null) {
if (answer instanceof Number) {
return (Number)answer;
}
if (answer instanceof String) {
try {
String text = (String)answer;
return NumberFormat.getInstance().parse(text);
} catch (ParseException var4) {
}
}
}
}
return null;
}
public static boolean isEmpty(Map<?, ?> map) {
return map == null || map.isEmpty();
}
public static boolean isNotEmpty(Map<?, ?> map) {
return !isEmpty(map);
}
}

View File

@ -0,0 +1,12 @@
package tech.powerjob.common.utils;
/**
* 毫无意义就是最大的意义
*
* @author tjq
* @since 2020/5/16
*/
@FunctionalInterface
public interface Meaningless {
void m() throws Exception;
}

View File

@ -0,0 +1,388 @@
package tech.powerjob.common.utils;
/*
Copyright [2020] [PowerJob]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import tech.powerjob.common.PowerJobDKey;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import java.io.IOException;
import java.net.*;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;
import static java.util.Collections.emptyList;
/**
* IP and Port Helper for RPC
*
* @author from dubbo, optimize by tjq
* @since 2020/8/8
*/
@Slf4j
public class NetUtils {
/**
* returned port range is [30000, 39999]
*/
private static final int RND_PORT_START = 30000;
private static final int RND_PORT_END = 65535;
private static volatile String HOST_ADDRESS;
private static final String LOCALHOST_VALUE = "127.0.0.1";
private static volatile InetAddress LOCAL_ADDRESS = null;
private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$");
private static final String ANY_HOST_VALUE = "0.0.0.0";
private NetUtils() {
}
public static int getRandomPort() {
return ThreadLocalRandom.current().nextInt(RND_PORT_START, RND_PORT_END);
}
/**
* 检测某个 IP 端口是否可用
* @param ip IP
* @param port 端口
* @return 是否可用
*/
public static boolean checkIpPortAvailable(String ip, int port) {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(ip, port), 1000);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 获取本机 IP 地址
*
* @return 本机 IP 地址
*/
public static String getLocalHost() {
return getLocalHostWithNetworkInterfaceChecker(null);
}
public static String getLocalHostWithNetworkInterfaceChecker(NetworkInterfaceChecker networkInterfaceChecker) {
if (HOST_ADDRESS != null) {
return HOST_ADDRESS;
}
String addressFromJVM = System.getProperty(PowerJobDKey.BIND_LOCAL_ADDRESS);
if (StringUtils.isNotEmpty(addressFromJVM)) {
log.info("[Net] use address from[{}]: {}", PowerJobDKey.BIND_LOCAL_ADDRESS, addressFromJVM);
return HOST_ADDRESS = addressFromJVM;
}
InetAddress address = getLocalAddress(networkInterfaceChecker);
if (address != null) {
return HOST_ADDRESS = address.getHostAddress();
}
return LOCALHOST_VALUE;
}
/**
* 隔离调用 scope核心场景才能直接调用 getLocalHost方便查看使用点
* @return IP
*/
public static String getLocalHost4Test() {
return getLocalHost();
}
/**
* Find first valid IP from local network card
*
* @return first valid local IP
*/
public static InetAddress getLocalAddress(NetworkInterfaceChecker networkInterfaceChecker) {
if (LOCAL_ADDRESS != null) {
return LOCAL_ADDRESS;
}
InetAddress localAddress = getLocalAddress0(networkInterfaceChecker);
LOCAL_ADDRESS = localAddress;
return localAddress;
}
private static InetAddress getLocalAddress0(NetworkInterfaceChecker networkInterfaceChecker) {
// @since 2.7.6, choose the {@link NetworkInterface} first
try {
InetAddress addressOp = getFirstReachableInetAddress( findNetworkInterface(networkInterfaceChecker));
if (addressOp != null) {
return addressOp;
}
} catch (Throwable e) {
log.warn("[Net] getLocalAddress0 failed.", e);
}
InetAddress localAddress = null;
try {
localAddress = InetAddress.getLocalHost();
Optional<InetAddress> addressOp = toValidAddress(localAddress);
if (addressOp.isPresent()) {
return addressOp.get();
}
} catch (Throwable e) {
log.warn("[Net] getLocalAddress0 failed.", e);
}
return localAddress;
}
private static InetAddress getFirstReachableInetAddress(NetworkInterface networkInterface) {
if(networkInterface == null ){
return null;
}
Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
if (addressOp.isPresent()) {
try {
if (addressOp.get().isReachable(100)) {
return addressOp.get();
}
} catch (IOException e) {
// ignore
}
}
}
return null;
}
/**
* Get the suitable {@link NetworkInterface}
*
* @return If no {@link NetworkInterface} is available , return <code>null</code>
* @since 2.7.6
*/
public static NetworkInterface findNetworkInterface(NetworkInterfaceChecker networkInterfaceChecker) {
List<NetworkInterface> validNetworkInterfaces = emptyList();
try {
validNetworkInterfaces = getValidNetworkInterfaces();
} catch (Throwable e) {
log.warn("[Net] findNetworkInterface failed", e);
}
// sort by interface index, the smaller is preferred.
validNetworkInterfaces.sort(Comparator.comparingInt(NetworkInterface::getIndex));
// Try to find the preferred one
for (NetworkInterface networkInterface : validNetworkInterfaces) {
if (isPreferredNetworkInterface(networkInterface)) {
log.info("[Net] use preferred network interface: {}", networkInterface);
return networkInterface;
}
if (isPassedCheckNetworkInterface(networkInterface, networkInterfaceChecker)) {
log.info("[Net] use PassedCheckNetworkInterface: {}", networkInterface);
return networkInterface;
}
}
// If not found, try to get the first one
for (NetworkInterface networkInterface : validNetworkInterfaces) {
InetAddress addressOp = getFirstReachableInetAddress(networkInterface);
if (addressOp != null) {
return networkInterface;
}
}
return first(validNetworkInterfaces);
}
/**
* 通过用户方法判断是否为目标网卡
* @param networkInterface networkInterface
* @param networkInterfaceChecker 判断方法
* @return true or false
*/
static boolean isPassedCheckNetworkInterface(NetworkInterface networkInterface, NetworkInterfaceChecker networkInterfaceChecker) {
if (networkInterfaceChecker == null) {
return false;
}
try {
boolean ok = networkInterfaceChecker.ok(networkInterface, getFirstReachableInetAddress(networkInterface));
log.info("[Net] try to choose NetworkInterface by NetworkInterfaceChecker, current NetworkInterface[{}], ok: {}", networkInterface, ok);
return ok;
} catch (Exception e) {
log.warn("[Net] isPassedCheckerNetworkInterface failed, current networkInterface: {}", networkInterface, e);
}
return false;
}
private static Optional<InetAddress> toValidAddress(InetAddress address) {
if (address instanceof Inet6Address) {
Inet6Address v6Address = (Inet6Address) address;
if (isPreferIPV6Address()) {
return Optional.ofNullable(normalizeV6Address(v6Address));
}
}
if (isValidV4Address(address)) {
return Optional.of(address);
}
return Optional.empty();
}
/**
* Check if an ipv6 address
*
* @return true if it is reachable
*/
static boolean isPreferIPV6Address() {
return Boolean.getBoolean("java.net.preferIPv6Addresses");
}
static boolean isValidV4Address(InetAddress address) {
if (address == null || address.isLoopbackAddress()) {
return false;
}
String name = address.getHostAddress();
return (name != null
&& IP_PATTERN.matcher(name).matches()
&& !ANY_HOST_VALUE.equals(name)
&& !LOCALHOST_VALUE.equals(name));
}
/**
* normalize the ipv6 Address, convert scope name to scope id.
* e.g.
* convert
* fe80:0:0:0:894:aeec:f37d:23e1%en0
* to
* fe80:0:0:0:894:aeec:f37d:23e1%5
* <p>
* The %5 after ipv6 address is called scope id.
* see java doc of {@link Inet6Address} for more details.
*
* @param address the input address
* @return the normalized address, with scope id converted to int
*/
static InetAddress normalizeV6Address(Inet6Address address) {
String addr = address.getHostAddress();
int i = addr.lastIndexOf('%');
if (i > 0) {
try {
return InetAddress.getByName(addr.substring(0, i) + '%' + address.getScopeId());
} catch (UnknownHostException e) {
// ignore
log.debug("Unknown IPV6 address: ", e);
}
}
return address;
}
/**
* Get the valid {@link NetworkInterface network interfaces}
*
* @return non-null
* @throws SocketException SocketException if an I/O error occurs.
* @since 2.7.6
*/
private static List<NetworkInterface> getValidNetworkInterfaces() throws SocketException {
List<NetworkInterface> validNetworkInterfaces = new LinkedList<>();
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement();
// ignore
if (ignoreNetworkInterface(networkInterface)) {
continue;
}
// 根据用户 -D 参数忽略网卡
if (ignoreInterfaceByConfig(networkInterface.getDisplayName()) || ignoreInterfaceByConfig(networkInterface.getName())) {
continue;
}
validNetworkInterfaces.add(networkInterface);
}
return validNetworkInterfaces;
}
/**
* @param networkInterface {@link NetworkInterface}
* @return if the specified {@link NetworkInterface} should be ignored, return <code>true</code>
* @throws SocketException SocketException if an I/O error occurs.
* @since 2.7.6
*/
private static boolean ignoreNetworkInterface(NetworkInterface networkInterface) throws SocketException {
return networkInterface == null
|| networkInterface.isLoopback()
|| networkInterface.isVirtual()
|| !networkInterface.isUp();
}
/**
* Take the first element from the specified collection
*
* @param values the collection object
* @param <T> the type of element of collection
* @return if found, return the first one, or <code>null</code>
* @since 2.7.6
*/
public static <T> T first(Collection<T> values) {
if (values == null || values.isEmpty()) {
return null;
}
if (values instanceof List) {
List<T> list = (List<T>) values;
return list.get(0);
} else {
return values.iterator().next();
}
}
/**
* Is preferred {@link NetworkInterface} or not
*
* @param networkInterface {@link NetworkInterface}
* @return if the name of the specified {@link NetworkInterface} matches
* the property value from {@link PowerJobDKey#PREFERRED_NETWORK_INTERFACE}, return <code>true</code>,
* or <code>false</code>
*/
public static boolean isPreferredNetworkInterface(NetworkInterface networkInterface) {
String preferredNetworkInterface = System.getProperty(PowerJobDKey.PREFERRED_NETWORK_INTERFACE);
if (Objects.equals(networkInterface.getDisplayName(), preferredNetworkInterface)) {
return true;
}
// 兼容直接使用网卡名称的情况,比如 Realtek PCIe GBE Family Controller
return Objects.equals(networkInterface.getName(), preferredNetworkInterface);
}
public static Pair<String, Integer> splitAddress2IpAndPort(String address) {
String[] split = address.split(":");
return Pair.of(split[0], Integer.valueOf(split[1]));
}
static boolean ignoreInterfaceByConfig(String interfaceName) {
String regex = System.getProperty(PowerJobDKey.IGNORED_NETWORK_INTERFACE_REGEX);
if (StringUtils.isBlank(regex)) {
return false;
}
if (interfaceName.matches(regex)) {
log.info("[Net] ignore network interface: {} by regex({})", interfaceName, regex);
return true;
}
return false;
}
@FunctionalInterface
public interface NetworkInterfaceChecker {
boolean ok(NetworkInterface networkInterface, InetAddress inetAddress);
}
}

View File

@ -0,0 +1,32 @@
package tech.powerjob.common.utils;
import org.apache.commons.lang3.StringUtils;
/**
* PropertyUtils
*
* @author tjq
* @since 2023/7/15
*/
public class PropertyUtils {
public static String readProperty(String key, String defaultValue) {
// 从启动参数读取
String property = System.getProperty(key);
if (StringUtils.isNotEmpty(property)) {
return property;
}
// 从 ENV 读取
property= System.getenv(key);
if (StringUtils.isNotEmpty(property)) {
return property;
}
// 部分操作系统不兼容 a.b.c 的环境变量,转换为 a_b_c 再取一次,即 PowerJob 支持 2 种类型的环境变量 key
property = System.getenv(key.replaceAll("\\.", "_"));
if (StringUtils.isNotEmpty(property)) {
return property;
}
return defaultValue;
}
}

View File

@ -0,0 +1,54 @@
package tech.powerjob.common.utils;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 分段锁
*
* @author tjq
* @since 2020/6/3
*/
@Slf4j
public class SegmentLock {
private final int mask;
private final Lock[] locks;
public SegmentLock(int concurrency) {
int size = CommonUtils.formatSize(concurrency);
mask = size - 1;
locks = new Lock[size];
for (int i = 0; i < size; i++) {
locks[i] = new ReentrantLock();
}
}
/**
* 阻塞获取锁,可被打断
* @param lockId 锁ID
* @throws InterruptedException 线程被中断异常
*/
public void lockInterruptible(int lockId) throws InterruptedException {
Lock lock = locks[lockId & mask];
lock.lockInterruptibly();
}
public void lockInterruptibleSafe(int lockId) {
try {
lockInterruptible(lockId);
}catch (InterruptedException ignore) {
}
}
/**
* 释放锁
* @param lockId 锁ID
*/
public void unlock(int lockId) {
Lock lock = locks[lockId & mask];
lock.unlock();
}
}

View File

@ -0,0 +1,26 @@
package tech.powerjob.common.utils;
/**
* Represents a supplier of results.
*
* <p>There is no requirement that a new or distinct result be returned each
* time the supplier is invoked.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #get()}.
*
* @param <T> the type of results supplied by this supplier
*
* @author tjq
* @since 2020/3/26
*/
@FunctionalInterface
public interface SupplierPlus<T> {
/**
* Gets a result.
* @return a result
* @throws Exception allow to throw Exception
*/
T get() throws Exception;
}

View File

@ -0,0 +1,50 @@
package tech.powerjob.common.utils;
import org.apache.commons.lang3.StringUtils;
import tech.powerjob.common.PowerJobDKey;
import tech.powerjob.common.enums.SpEnv;
/**
* 系统工具
*
* @author tjq
* @since 2025/8/17
*/
public class SysUtils {
/**
* 可用处理器核数
* @return 可用处理器核数
*/
public static int availableProcessors() {
String property = System.getProperty(PowerJobDKey.SYS_AVAILABLE_PROCESSORS);
if (StringUtils.isEmpty(property)) {
return Runtime.getRuntime().availableProcessors();
}
return Integer.parseInt(property);
}
/**
* 判断是否为测试环境
* @return 测试环境
*/
public static boolean isTestEnv() {
String property = System.getProperty(PowerJobDKey.SP_ENV);
if (StringUtils.isEmpty(property)) {
return false;
}
return SpEnv.TEST.getCode().equalsIgnoreCase(property);
}
/**
* 判断是否为试用环境
* @return 试用环境
*/
public static boolean isTrialEnv() {
String property = System.getProperty(PowerJobDKey.SP_ENV);
if (StringUtils.isEmpty(property)) {
return false;
}
return SpEnv.TRIAL.getCode().equalsIgnoreCase(property);
}
}

View File

@ -0,0 +1,14 @@
package tech.powerjob.common.utils.net;
import java.io.Closeable;
/**
* socket 服务器,用于进行连通性测试
*
* @author tjq
* @since 2024/2/8
*/
public interface PingPongServer extends Closeable {
void initialize(int port) throws Exception;
}

View File

@ -0,0 +1,63 @@
package tech.powerjob.common.utils.net;
import lombok.extern.slf4j.Slf4j;
import tech.powerjob.common.utils.CommonUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* 简易服务器
*
* @author tjq
* @since 2024/2/8
*/
@Slf4j
public class PingPongSocketServer implements PingPongServer {
private Thread thread;
private ServerSocket serverSocket;
private volatile boolean terminated = false;
@Override
public void initialize(int port) throws Exception{
serverSocket = new ServerSocket(port);
thread = new Thread(() -> {
while (true) {
if (terminated) {
return;
}
// 接收连接如果没有连接accept() 方法会阻塞
try (Socket socket = serverSocket.accept();OutputStream outputStream = socket.getOutputStream();) {
socket.setSoTimeout(2000);
socket.setKeepAlive(false);
outputStream.write(PingPongUtils.PONG.getBytes(StandardCharsets.UTF_8));
// BufferedReader.readLine() 会等待直到遇到换行符(\n或回车符\r\n才会返回一行内容。如果服务器发送的数据没有这些换行符readLine() 会一直阻塞,直到超时
outputStream.write(System.lineSeparator().getBytes(StandardCharsets.UTF_8));
outputStream.flush();
} catch (Exception e) {
if (!terminated) {
log.warn("[PingPongSocketServer] process accepted socket failed!", e);
}
}
}
}, "PingPongSocketServer-Thread");
thread.start();
}
@Override
public void close() throws IOException {
terminated = true;
CommonUtils.executeIgnoreException(() -> serverSocket.close());
thread.interrupt();
}
}

View File

@ -0,0 +1,56 @@
package tech.powerjob.common.utils.net;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
/**
* socket 连通性助手
*
* @author tjq
* @since 2024/2/8
*/
@Slf4j
public class PingPongUtils {
static final String PING = "ping";
static final String PONG = "pong";
/**
* 验证目标 IP 和 端口的连通性
* @param targetIp 目标 IP
* @param targetPort 目标端口
* @return true or false
*/
public static boolean checkConnectivity(String targetIp, int targetPort) {
try (Socket s = new Socket(targetIp, targetPort);InputStream is = s.getInputStream();OutputStream os = s.getOutputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
s.setSoTimeout(2000);
s.setKeepAlive(false);
// 发送 PING 请求
os.write(PING.getBytes(StandardCharsets.UTF_8));
os.flush();
//读取服务器返回的消息
String content = br.readLine();
if (PONG.equalsIgnoreCase(content)) {
return true;
}
} catch (UnknownHostException e) {
log.warn("[SocketConnectivityUtils] unknown host: {}:{}", targetIp, targetPort);
} catch (IOException e) {
log.warn("[SocketConnectivityUtils] IOException: {}:{}, msg: {}", targetIp, targetPort, ExceptionUtils.getMessage(e));
} catch (Exception e) {
log.error("[SocketConnectivityUtils] unknown exception for check ip: {}:{}", targetIp, targetPort, e);
}
return false;
}
}

View File

@ -0,0 +1,30 @@
import tech.powerjob.common.PowerJobDKey;
import tech.powerjob.common.utils.NetUtils;
import org.junit.jupiter.api.Test;
/**
* NetUtilsTest
*
* @author tjq
* @since 2020/8/8
*/
public class NetUtilsTest {
@Test
public void testOrigin() {
System.out.println(NetUtils.getLocalHost4Test());
}
@Test
public void testPreferredNetworkInterface() {
System.setProperty(PowerJobDKey.PREFERRED_NETWORK_INTERFACE, "en5");
System.out.println(NetUtils.getLocalHost4Test());
}
@Test
public void testIgnoredNetworkInterface() {
System.setProperty(PowerJobDKey.IGNORED_NETWORK_INTERFACE_REGEX, "utun.|llw.");
System.out.println(NetUtils.getLocalHost4Test());
}
}

View File

@ -0,0 +1,60 @@
import tech.powerjob.common.utils.SegmentLock;
import org.junit.jupiter.api.Test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 分段锁测试
*
* @author tjq
* @since 2020/6/15
*/
public class SegmentLockTest {
@Test
public void testLock() throws Exception {
int lockId = 10086;
SegmentLock lock = new SegmentLock(4);
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(() -> {
System.out.println("before lock A");
lock.lockInterruptibleSafe(lockId);
System.out.println("after lock A");
});
pool.execute(() -> {
System.out.println("before lock AA");
lock.lockInterruptibleSafe(lockId);
System.out.println("after lock AA");
});
Thread.sleep(10000);
}
@Test
public void testUnLock() throws Exception {
int lockId = 10086;
SegmentLock lock = new SegmentLock(4);
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.execute(() -> {
System.out.println("before lock A");
lock.lockInterruptibleSafe(lockId);
System.out.println("after lock A");
try {
Thread.sleep(5000);
}catch (Exception ignore) {
}
lock.unlock(lockId);
});
pool.execute(() -> {
System.out.println("before lock AA");
lock.lockInterruptibleSafe(lockId);
System.out.println("after lock AA");
});
Thread.sleep(10000);
}
}

View File

@ -0,0 +1,81 @@
package tech.powerjob.common.serialize;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Map;
/**
* test json utils
*
* @author tjq
* @since 2024/3/16
*/
@Slf4j
class JsonUtilsTest {
@Test
@SneakyThrows
void simpleTest() {
Person person = new Person().setName("mubao").setAge(18);
String jsonString = JsonUtils.toJSONString(person);
log.info("[JsonUtilsTest] person: {}, jsonString: {}", person, jsonString);
assert jsonString != null;
Person person2 = JsonUtils.parseObject(jsonString, Person.class);
assert person2.equals(person);
}
@Test
@SneakyThrows
void testAdvanceApi() {
PersonPlus personPlus = new PersonPlus();
personPlus.setName("gongbao").setAge(3);
personPlus.setBirthDay(LocalDateTime.now());
String jsonString = JsonUtils.toJSONString(personPlus);
PersonPlus personPlus2 = JsonUtils.parseObject(jsonString, PersonPlus.class);
assert personPlus2.equals(personPlus);
}
@Test
@SneakyThrows
void testMoreOrLessFields() {
PersonPlus personPlus = new PersonPlus().setBirthDay(LocalDateTime.now());
personPlus.setName("gongbao").setAge(3);
String originJsonStr = JsonUtils.toJSONString(personPlus);
Map<String, Object> personPlusMapMore = JsonUtils.parseMap(originJsonStr);
personPlusMapMore.put("extraKey", System.currentTimeMillis());
PersonPlus personPlusByMoreFieldsJsonStr = JsonUtils.parseObject(JsonUtils.toJSONString(personPlusMapMore), PersonPlus.class);
assert personPlusByMoreFieldsJsonStr.equals(personPlus);
Map<String, Object> personPlusMapLess = JsonUtils.parseMap(originJsonStr);
personPlusMapLess.remove("birthDay");
PersonPlus personPlusByLessFieldsJsonStr = JsonUtils.parseObject(JsonUtils.toJSONString(personPlusMapLess), PersonPlus.class);
assert personPlusByLessFieldsJsonStr.getName().equals(personPlus.getName());
assert personPlusByLessFieldsJsonStr.getAge().equals(personPlus.getAge());
}
@Data
@Accessors(chain = true)
static class Person implements Serializable {
private String name;
private Integer age;
}
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
static class PersonPlus extends Person {
private LocalDateTime birthDay;
}
}

View File

@ -0,0 +1,39 @@
package tech.powerjob.common.utils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.junit.jupiter.api.Test;
import tech.powerjob.common.exception.PowerJobException;
import java.util.Collections;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
* CommonUtilsTest
*
* @author tjq
* @since 2024/8/11
*/
class CommonUtilsTest {
@Test
void testRequireNonNull() {
assertThrowsExactly(PowerJobException.class, () -> CommonUtils.requireNonNull(null, "NULL_OBJ"));
assertThrowsExactly(PowerJobException.class, () -> CommonUtils.requireNonNull("", "EMPTY_STR"));
assertThrowsExactly(PowerJobException.class, () -> CommonUtils.requireNonNull(Lists.newArrayList(), "EMPTY_COLLECTION"));
assertThrowsExactly(PowerJobException.class, () -> CommonUtils.requireNonNull(Collections.emptyMap(), "EMPTY_MAP"));
Map<String, Object> map = Maps.newHashMap();
map.put("1", 1);
CommonUtils.requireNonNull(1, "NORMAL");
CommonUtils.requireNonNull("1", "NORMAL");
CommonUtils.requireNonNull(Lists.newArrayList("1"), "NORMAL");
CommonUtils.requireNonNull(map, "NORMAL");
}
}

View File

@ -0,0 +1,25 @@
package tech.powerjob.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;
import static org.junit.jupiter.api.Assertions.*;
/**
* Java 语言相关的工具测试
*
* @author tjq
* @since 2022/10/23
*/
@Slf4j
class JavaUtilsTest {
@Test
void determinePackageVersion() {
String packageVersion = JavaUtils.determinePackageVersion(LoggerFactory.class);
log.info("[determinePackageVersion] LoggerFactory's package version: {}", packageVersion);
}
}

View File

@ -0,0 +1,31 @@
package tech.powerjob.common.utils.net;
import org.junit.jupiter.api.Test;
import tech.powerjob.common.utils.NetUtils;
/**
* desc
*
* @author tjq
* @since 2024/2/8
*/
class PingPongSocketServerTest {
@Test
void test() throws Exception {
int port = 8877;
PingPongSocketServer pingPongSocketServer = new PingPongSocketServer();
pingPongSocketServer.initialize(port);
System.out.println("[PingPongSocketServerTest] finished initialize");
assert PingPongUtils.checkConnectivity(NetUtils.getLocalHost(), port);
assert !PingPongUtils.checkConnectivity(NetUtils.getLocalHost(), port + 1);
pingPongSocketServer.close();
System.out.println("[PingPongSocketServerTest] finished close");
}
}