---初始化项目
This commit is contained in:
98
powerjob-common/pom.xml
Normal file
98
powerjob-common/pom.xml
Normal 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>
|
||||
@ -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";
|
||||
}
|
||||
@ -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";
|
||||
}
|
||||
@ -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";
|
||||
}
|
||||
@ -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";
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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 {
|
||||
}
|
||||
@ -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_side(s4w == 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";
|
||||
|
||||
}
|
||||
@ -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";
|
||||
}
|
||||
@ -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";
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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";
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package tech.powerjob.common.exception;
|
||||
|
||||
/**
|
||||
* ImpossibleException
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2023/7/12
|
||||
*/
|
||||
public class ImpossibleException extends RuntimeException {
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 所属应用ID(OpenClient不需要用户填写,自动填充)
|
||||
*/
|
||||
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");
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
/**
|
||||
* 最低内存空间,单位 GB,0代表不限
|
||||
*/
|
||||
private double minMemorySpace;
|
||||
/**
|
||||
* 最低磁盘空间,单位 GB,0代表不限
|
||||
*/
|
||||
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;
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package tech.powerjob.common.response;
|
||||
|
||||
/**
|
||||
* 主要目的:消除 idea 烦人的类型提示
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2023/9/2
|
||||
*/
|
||||
public class ObjectResultDTO extends ResultDTO<Object> {
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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.0:ThreadLocal + 手动设置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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package tech.powerjob.common.utils;
|
||||
|
||||
/**
|
||||
* 毫无意义就是最大的意义
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2020/5/16
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Meaningless {
|
||||
void m() throws Exception;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
30
powerjob-common/src/test/java/NetUtilsTest.java
Normal file
30
powerjob-common/src/test/java/NetUtilsTest.java
Normal 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());
|
||||
}
|
||||
|
||||
}
|
||||
60
powerjob-common/src/test/java/SegmentLockTest.java
Normal file
60
powerjob-common/src/test/java/SegmentLockTest.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user