diff --git a/docs/configs/docsdev.js b/docs/configs/docsdev.js index d7cac77fa9..3dfd9b3166 100644 --- a/docs/configs/docsdev.js +++ b/docs/configs/docsdev.js @@ -185,6 +185,10 @@ export default { title: 'Dinky', link: '/en-us/docs/dev/user_doc/guide/task/dinky.html', }, + { + title: 'Java', + link: '/en-us/docs/dev/user_doc/guide/task/java.html', + }, { title: 'SageMaker', link: '/en-us/docs/dev/user_doc/guide/task/sagemaker.html', @@ -809,6 +813,10 @@ export default { title: 'Dinky', link: '/zh-cn/docs/dev/user_doc/guide/task/dinky.html', }, + { + title: 'Java', + link: '/zh-cn/docs/dev/user_doc/guide/task/java.html', + }, { title: 'SageMaker', link: '/zh-cn/docs/dev/user_doc/guide/task/sagemaker.html', diff --git a/docs/docs/en/guide/task/java.md b/docs/docs/en/guide/task/java.md new file mode 100644 index 0000000000..95bf5c28f9 --- /dev/null +++ b/docs/docs/en/guide/task/java.md @@ -0,0 +1,47 @@ +# Overview + +This node is for executing java-type tasks and supports using files and jar packages as program entries. + +# Create Tasks + +- Click on `Project Management` -> `Project Name` -> `Workflow Definition`, click on the “Create workflow” button, go to the DAG edit page: + +- Drag the toolbar's Java task node to the palette. + +# Task Parameters +| **Parameter** | **Description** | +|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Node Name | The name of the set task. The node name in a workflow definition is unique. | +| Run Flag | Indicates whether the node is scheduled properly and turns on the kill switch, if not needed. | +| Description | Describes the functionality of the node. | +| Task Priority | When the number of worker threads is insufficient, the worker executes tasks according to the priority. When the priority is the same, the worker executes tasks by order. | +| Worker Group | The group of machines who execute the tasks. If selecting `Default`, DolphinScheduler will randomly choose a worker machine to execute the task. | +| Environment Name | Configure the environment in which the task runs. | +| Number Of Failed Retries | Number of resubmitted tasks that failed. You can choose the number in the drop-down menu or fill it manually. | +| Failed Retry Interval | the interval between the failure and resubmission of a task. You can choose the number in the drop-down menu or fill it manually. | +| Delayed Execution Time | the amount of time a task is delayed, in units. | +| Timeout Alarm | Check timeout warning, timeout failure, when the task exceeds the“Timeout length”, send a warning message and the task execution fails. | +| Module Path | pick Java 9 + 's modularity feature, put all resources into-module-path, and require that the JDK version in your worker supports modularity. | +| Main Parameter | Java program main method entry parameter. | +| Java VM Parameters | JVM startup parameters. | +| Script | You need to write Java code if you use the Java run type. The public class must exist in the code without writing a package statement. | +| Resources | External JAR packages or other resource files that are added to the classpath or module path and can be easily retrieved in your JAVA script. | +| Custom parameter | A user-defined parameter that is part of HTTP and replaces `${ variable }` in the script . | +| Pre Tasks | Selects a pre-task for the current task and sets the pre-task as the upstream of the current task. | + +## Example + +Java type tasks have two modes of execution, here is a demonstration of executing tasks in Java mode. + +The main configuration parameters are as follows: +- Run Type +- Module Path +- Main Parameters +- Java VM Parameters +- Script + +![java_task](../../../../img/tasks/demo/java_task02.png) + +## Note + +When you run the task in JAVA execution mode, the public class must exist in the code, and you could omit writing a package statement. diff --git a/docs/docs/zh/guide/task/java.md b/docs/docs/zh/guide/task/java.md new file mode 100644 index 0000000000..852886dae4 --- /dev/null +++ b/docs/docs/zh/guide/task/java.md @@ -0,0 +1,50 @@ +# JAVA 节点 + +## 综述 + +该节点用于执行 java 类型的任务,支持使用单文件和jar包作为程序入口。 + +## 创建任务 + +- 点击项目管理 -> 项目名称 -> 工作流定义,点击”创建工作流”按钮,进入 DAG 编辑页面: + +- 拖动工具栏的JAVA任务节点到画板中。 + +## 任务参数 + +- 节点名称:设置任务的名称。一个工作流定义中的节点名称是唯一的。 +- 运行标志:标识这个节点是否能正常调度,如果不需要执行,可以打开禁止执行开关。 +- 描述:描述该节点的功能。 +- 任务优先级:worker 线程数不足时,根据优先级从高到低依次执行,优先级一样时根据先进先出原则执行。 +- Worker 分组:任务分配给 worker 组的机器机执行,选择 Default,会随机选择一台 worker 机执行。 +- 环境名称:配置运行任务的环境。 +- 失败重试次数:任务失败重新提交的次数,支持下拉和手填。 +- 失败重试间隔:任务失败重新提交任务的时间间隔,支持下拉和手填。 +- 延迟执行时间:任务延迟执行的时间,以分为单位。 +- 超时告警:勾选超时告警、超时失败,当任务超过"超时时长"后,会发送告警邮件并且任务执行失败。 +- 模块路径:开启使用JAVA9+的模块化特性,把所有资源放入--module-path中,要求您的worker中的JDK版本支持模块化。 +- 主程序参数:作为普通Java程序main方法入口参数。 +- 虚拟机参数:配置启动虚拟机参数。 +- 脚本:若使用JAVA运行类型则需要编写JAVA代码。代码中必须存在public类,不用写package语句。 +- 资源:可以是外部JAR包也可以是其他资源文件,它们都会被加入到类路径或模块路径中,您可以在自己的JAVA脚本中轻松获取。 +- 自定义参数:是 http 局部的用户自定义参数,会替换脚本中以 ${变量} 的内容。 +- 前置任务:选择当前任务的前置任务,会将被选择的前置任务设置为当前任务的上游。 + +## 任务样例 + +java任务类型有两种运行模式,这里以JAVA模式为例进行演示。 + +主要配置参数如下: + +- 运行类型 +- 模块路径 +- 主程序参数 +- 虚拟机参数 +- 脚本文件 + +![java_task](../../../../img/tasks/demo/java_task02.png) + +## 注意事项 + +使用JAVA运行类型时代码中必须存在public类,可以不写package语句 + diff --git a/docs/img/tasks/demo/java_task02.png b/docs/img/tasks/demo/java_task02.png new file mode 100644 index 0000000000..28cc4beb28 Binary files /dev/null and b/docs/img/tasks/demo/java_task02.png differ diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-all/pom.xml b/dolphinscheduler-task-plugin/dolphinscheduler-task-all/pom.xml index 665d28783d..ff252760ab 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-all/pom.xml +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-all/pom.xml @@ -189,6 +189,12 @@ ${project.version} + + org.apache.dolphinscheduler + dolphinscheduler-task-java + ${project.version} + + org.apache.dolphinscheduler dolphinscheduler-task-sagemaker diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/TaskResponse.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/TaskResponse.java index e4b60c1928..241f308dea 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/TaskResponse.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/TaskResponse.java @@ -19,6 +19,9 @@ package org.apache.dolphinscheduler.plugin.task.api.model; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskRunStatus; +import lombok.Data; + +@Data public class TaskResponse { /** @@ -46,8 +49,6 @@ public class TaskResponse { */ private Process process; - - /** * cancel */ @@ -59,68 +60,4 @@ public class TaskResponse { private volatile int exitStatusCode = -1; private TaskRunStatus status; - - public String getVarPool() { - return varPool; - } - - public void setVarPool(String varPool) { - this.varPool = varPool; - } - - public int getProcessId() { - return processId; - } - - public void setProcessId(int processId) { - this.processId = processId; - } - - public String getResultString() { - return resultString; - } - - public void setResultString(String resultString) { - this.resultString = resultString; - } - - public String getAppIds() { - return appIds; - } - - public void setAppIds(String appIds) { - this.appIds = appIds; - } - - public boolean isCancel() { - return cancel; - } - - public void setCancel(boolean cancel) { - this.cancel = cancel; - } - - public int getExitStatusCode() { - return exitStatusCode; - } - - public void setExitStatusCode(int exitStatusCode) { - this.exitStatusCode = exitStatusCode; - } - - public Process getProcess() { - return process; - } - - public void setProcess(Process process) { - this.process = process; - } - - public TaskRunStatus getStatus() { - return status; - } - - public void setStatus(TaskRunStatus status) { - this.status = status; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/pom.xml b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/pom.xml new file mode 100644 index 0000000000..36aa9afeb2 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/pom.xml @@ -0,0 +1,45 @@ + + + + + dolphinscheduler-task-plugin + org.apache.dolphinscheduler + dev-SNAPSHOT + + + 4.0.0 + dolphinscheduler-task-java + jar + + + + + org.apache.dolphinscheduler + dolphinscheduler-spi + + + + org.apache.dolphinscheduler + dolphinscheduler-task-api + + + + + \ No newline at end of file diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaConstants.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaConstants.java new file mode 100644 index 0000000000..e4d941db30 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaConstants.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.task.java; + +import java.io.File; + +public class JavaConstants { + + private JavaConstants() { + throw new IllegalStateException("Utility class"); + } + + /** + * The constants used to get the Java installation directory + **/ + public static final String JAVA_HOME_VAR = "${JAVA_HOME}"; + + /** + * this constant represents the use of the java command to run a task + **/ + public static final String RUN_TYPE_JAVA = "JAVA"; + + /** + * this constant represents the use of the java -jar command to run a task + **/ + public static final String RUN_TYPE_JAR = "JAR"; + + /** + * This constant is the Classpath or module path delimiter for different operating systems + **/ + public static final String PATH_SEPARATOR = System.getProperty("path.separator"); + + /** + * This constant represents the current directory in the Classpath or module path + **/ + public static final String CLASSPATH_CURRENT_DIR = "."; + + /** + * This constant is used to construct the pre-pathname of the Java source file + **/ + public static final String JAVA_SOURCE_CODE_NAME_TEMPLATE = "%s/%s.java"; + + /** + * This constant is the regular expression to get the class name of the source file + **/ + public static final String PUBLIC_CLASS_NAME_REGEX = "(.*\\s*public\\s+class\\s+)([a-zA-Z_]+[//w_]*)([.\\s\\S]*)"; +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaParameters.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaParameters.java new file mode 100644 index 0000000000..527441b634 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaParameters.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.task.java; + +import org.apache.dolphinscheduler.plugin.task.api.model.ResourceInfo; +import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; +import org.apache.dolphinscheduler.spi.utils.StringUtils; + +import java.util.List; + +import lombok.Data; + +@Data +public class JavaParameters extends AbstractParameters { + /** + * origin java script + */ + private String rawScript; + + /** + * run in jar file + */ + private ResourceInfo mainJar; + + /** + * Marks the current task running mode + */ + private String runType; + + /** + * main method args + **/ + private String mainArgs; + + /** + * java virtual machine args + **/ + private String jvmArgs; + + /** + * module path or class path flag + **/ + private boolean isModulePath; + + /** + * resource list + */ + private List resourceList; + + /** + * Check that the parameters are valid + * + * @returnboolean + */ + @Override + public boolean checkParameters() { + return runType != null && (StringUtils.isNotBlank(rawScript) || mainJar != null); + } + + /** + * Gets a list of known resource files + * + * @return List + **/ + @Override + public List getResourceFilesList() { + return this.resourceList; + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTask.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTask.java new file mode 100644 index 0000000000..e23eb5124c --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTask.java @@ -0,0 +1,388 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.task.java; + +import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.SINGLE_SLASH; +import static org.apache.dolphinscheduler.plugin.task.java.JavaConstants.JAVA_HOME_VAR; +import static org.apache.dolphinscheduler.plugin.task.java.JavaConstants.PUBLIC_CLASS_NAME_REGEX; + +import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; +import org.apache.dolphinscheduler.plugin.task.api.ShellCommandExecutor; +import org.apache.dolphinscheduler.plugin.task.api.TaskCallBack; +import org.apache.dolphinscheduler.plugin.task.api.TaskConstants; +import org.apache.dolphinscheduler.plugin.task.api.TaskException; +import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; +import org.apache.dolphinscheduler.plugin.task.api.model.Property; +import org.apache.dolphinscheduler.plugin.task.api.model.ResourceInfo; +import org.apache.dolphinscheduler.plugin.task.api.model.TaskResponse; +import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; +import org.apache.dolphinscheduler.plugin.task.api.parser.ParamUtils; +import org.apache.dolphinscheduler.plugin.task.api.parser.ParameterUtils; +import org.apache.dolphinscheduler.plugin.task.api.utils.MapUtils; +import org.apache.dolphinscheduler.plugin.task.java.exception.JavaSourceFileExistException; +import org.apache.dolphinscheduler.plugin.task.java.exception.PublicClassNotFoundException; +import org.apache.dolphinscheduler.plugin.task.java.exception.RunTypeNotFoundException; +import org.apache.dolphinscheduler.spi.utils.JSONUtils; + +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.common.base.Preconditions; + +public class JavaTask extends AbstractTask { + + /** + * Contains various parameters for this task + */ + private JavaParameters javaParameters; + + /** + * To run shell commands + */ + private ShellCommandExecutor shellCommandExecutor; + + /** + * task execution context + */ + private TaskExecutionContext taskRequest; + + /** + * class name regex pattern + */ + private static final Pattern classNamePattern = Pattern.compile(PUBLIC_CLASS_NAME_REGEX); + + public JavaTask(TaskExecutionContext taskRequest) { + super(taskRequest); + this.taskRequest = taskRequest; + this.shellCommandExecutor = new ShellCommandExecutor(this::logHandle, + taskRequest, + logger); + } + + /** + * Initializes a Java task + * @return void + **/ + @Override + public void init() { + logger.info("java task params {}", taskRequest.getTaskParams()); + javaParameters = JSONUtils.parseObject(taskRequest.getTaskParams(), JavaParameters.class); + if (javaParameters == null || !javaParameters.checkParameters()) { + throw new TaskException("java task params is not valid"); + } + if (javaParameters.getRunType().equals(JavaConstants.RUN_TYPE_JAR)) { + setMainJarName(); + } + } + + /** + * Gets the Java source file that was initially processed + * + * @return String + **/ + @Override + public String getPreScript() { + String rawJavaScript = javaParameters.getRawScript().replaceAll("\\r\\n", "\n"); + try { + rawJavaScript = convertJavaSourceCodePlaceholders(rawJavaScript); + } catch (StringIndexOutOfBoundsException e) { + logger.error("setShareVar field format error, raw java script: {}", rawJavaScript); + } + return rawJavaScript; + } + + /** + * Execute Java tasks + * + * @return void + * @throws Exception + */ + @Override + public void handle(TaskCallBack taskCallBack) throws TaskException { + try { + // Step 1: judge if is java or jar run type. + // Step 2 case1: the jar run type builds the command directly, adding resource to the java -jar class when building the command + // Step 2 case2: the java run type, first replace the custom parameters, then compile the code, and then build the command will add resource + // Step 3: to run the command + String command = null; + switch (javaParameters.getRunType()) { + case JavaConstants.RUN_TYPE_JAVA: + command = buildJavaCommand(); + break; + case JavaConstants.RUN_TYPE_JAR: + command = buildJarCommand(); + break; + default: + throw new RunTypeNotFoundException("run type is required, but it is null now."); + } + Preconditions.checkNotNull(command, "command not be null."); + TaskResponse taskResponse = shellCommandExecutor.run(command); + logger.info("java task run result: {}", taskResponse); + setExitStatusCode(taskResponse.getExitStatusCode()); + setAppIds(taskResponse.getAppIds()); + setProcessId(taskResponse.getProcessId()); + setVarPool(shellCommandExecutor.getVarPool()); + } catch (InterruptedException e) { + logger.error("java task interrupted ", e); + setExitStatusCode(TaskConstants.EXIT_CODE_FAILURE); + Thread.currentThread().interrupt(); + } catch (RunTypeNotFoundException e) { + logger.error(e.getMessage()); + setExitStatusCode(TaskConstants.EXIT_CODE_FAILURE); + throw e; + } catch (Exception e) { + logger.error("java task failed ", e); + setExitStatusCode(TaskConstants.EXIT_CODE_FAILURE); + throw new TaskException("run java task error", e); + } + } + + /** + * Construct a shell command for the java Run mode + * + * @return String + * @throws Exception + **/ + protected String buildJavaCommand() throws Exception { + StringBuilder builder = new StringBuilder(); + String sourceCode = buildJavaSourceContent(); + builder.append(buildJavaCompileCommand(sourceCode)) + .append(";") + .append(getJavaCommandPath()) + .append("java").append(" ") + .append(buildResourcePath()) + .append(" ") + .append(getPublicClassName(sourceCode)) + .append(" ") + .append(javaParameters.getMainArgs().trim()).append(" ") + .append(javaParameters.getJvmArgs().trim()); + return builder.toString(); + } + + private void setMainJarName() { + ResourceInfo mainJar = javaParameters.getMainJar(); + String resourceName = getResourceNameOfMainJar(mainJar); + mainJar.setRes(resourceName); + javaParameters.setMainJar(mainJar); + } + + /** + * Construct a shell command for the java -jar Run mode + * + * @return String + **/ + protected String buildJarCommand() { + String fullName = javaParameters.getMainJar().getResourceName(); + String mainJarName = fullName.substring(0, fullName.lastIndexOf('.')); + mainJarName = mainJarName.substring(mainJarName.lastIndexOf('.') + 1) + ".jar"; + StringBuilder builder = new StringBuilder(); + builder.append(getJavaCommandPath()) + .append("java").append(" ") + .append(buildResourcePath()).append(" ") + .append("-jar").append(" ") + .append(taskRequest.getExecutePath()) + .append(mainJarName).append(" ") + .append(javaParameters.getMainArgs().trim()).append(" ") + .append(javaParameters.getJvmArgs().trim()); + return builder.toString(); + } + + private String getResourceNameOfMainJar(ResourceInfo mainJar) { + if (null == mainJar) { + throw new RuntimeException("The jar for the task is required."); + } + return mainJar.getId() == 0 + ? mainJar.getRes() + // when update resource maybe has error + : mainJar.getResourceName().replaceFirst(SINGLE_SLASH, ""); + } + + @Override + public void cancel() throws TaskException { + // cancel process + try { + shellCommandExecutor.cancelApplication(); + } catch (Exception e) { + throw new TaskException(); + } + } + + @Override + public AbstractParameters getParameters() { + return javaParameters; + } + + + /** + * Replaces placeholders such as local variables in source files + * + * @param rawScript + * @return String + * @throws StringIndexOutOfBoundsException + */ + protected static String convertJavaSourceCodePlaceholders(String rawScript) throws StringIndexOutOfBoundsException { + int len = "${setShareVar(${".length(); + + int scriptStart = 0; + while ((scriptStart = rawScript.indexOf("${setShareVar(${", scriptStart)) != -1) { + int start = -1; + int end = rawScript.indexOf('}', scriptStart + len); + String prop = rawScript.substring(scriptStart + len, end); + + start = rawScript.indexOf(',', end); + end = rawScript.indexOf(')', start); + + String value = rawScript.substring(start + 1, end); + + start = rawScript.indexOf('}', start) + 1; + end = rawScript.length(); + + String replaceScript = String.format("print(\"${{setValue({},{})}}\".format(\"%s\",%s))", prop, value); + + rawScript = rawScript.substring(0, scriptStart) + replaceScript + rawScript.substring(start, end); + + scriptStart += replaceScript.length(); + } + return rawScript; + } + + /** + * Creates a Java source file when it does not exist + * + * @param sourceCode + * @param fileName + * @return String + **/ + protected void createJavaSourceFileIfNotExists(String sourceCode, String fileName) throws IOException { + logger.info("tenantCode: {}, task dir:{}", taskRequest.getTenantCode(), taskRequest.getExecutePath()); + if (!Files.exists(Paths.get(fileName))) { + logger.info("the java source code:{}, will be write to the file: {}", fileName,sourceCode); + // write data to file + FileUtils.writeStringToFile(new File(fileName), + sourceCode, + StandardCharsets.UTF_8); + } else { + throw new JavaSourceFileExistException("java source file exists, please report an issue on official."); + } + } + + /** + * Construct the full path name of the Java source file from the temporary execution path of the task + * + * @return String + **/ + protected String buildJavaSourceCodeFileFullName(String publicClassName) { + return String.format(JavaConstants.JAVA_SOURCE_CODE_NAME_TEMPLATE, taskRequest.getExecutePath(), publicClassName); + } + + /** + * Construct a Classpath or module path based on isModulePath + * + * @return String + **/ + protected String buildResourcePath() { + StringBuilder builder = new StringBuilder(); + if (javaParameters.isModulePath()) { + builder.append("--module-path"); + } else { + builder.append("--class-path"); + } + builder.append(" ").append(JavaConstants.CLASSPATH_CURRENT_DIR) + .append(JavaConstants.PATH_SEPARATOR) + .append(taskRequest.getExecutePath()); + for (ResourceInfo info : javaParameters.getResourceFilesList()) { + builder.append(JavaConstants.PATH_SEPARATOR); + builder.append(taskRequest.getExecutePath()) + .append(info.getResourceName()); + } + return builder.toString(); + } + + /** + * Constructs a shell command compiled from a Java source file + * + * @param sourceCode + * @return String + * @throws IOException + **/ + protected String buildJavaCompileCommand(String sourceCode) throws IOException { + String publicClassName = getPublicClassName(sourceCode); + String fileName = buildJavaSourceCodeFileFullName(publicClassName); + createJavaSourceFileIfNotExists(sourceCode, fileName); + + StringBuilder compilerCommand = new StringBuilder() + .append(getJavaCommandPath()) + .append("javac").append(" ") + .append(buildResourcePath()).append(" ") + .append(fileName); + return compilerCommand.toString(); + } + + /** + * Work with Java source file content, such as replacing local variables + * + * @return String + **/ + protected String buildJavaSourceContent() { + String rawJavaScript = javaParameters.getRawScript().replaceAll("\\r\\n", "\n"); + // replace placeholder + + Map paramsMap = taskRequest.getPrepareParamsMap(); + if (MapUtils.isEmpty(paramsMap)) { + paramsMap = new HashMap<>(); + } + if (MapUtils.isNotEmpty(taskRequest.getParamsMap())) { + paramsMap.putAll(taskRequest.getParamsMap()); + } + logger.info("The current java source code will begin to replace the placeholder: {}", rawJavaScript); + rawJavaScript = ParameterUtils.convertParameterPlaceholders(rawJavaScript, ParamUtils.convert(paramsMap)); + return rawJavaScript; + } + + /** + * Gets the operating system absolute path to the Java command + * + * @return String + **/ + private String getJavaCommandPath() { + return JAVA_HOME_VAR + File.separator + "bin" + File.separator; + } + + /** + * Gets the public class name from the Java source file + * + * @param sourceCode + * @return String + **/ + public String getPublicClassName(String sourceCode) { + Matcher matcher = classNamePattern.matcher(sourceCode); + if (!matcher.find()) { + throw new PublicClassNotFoundException("public class is not be found in source code : " + sourceCode); + } + return matcher.group(2).trim(); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannel.java new file mode 100644 index 0000000000..32407b204a --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannel.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.task.java; + +import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; +import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; +import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; +import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; +import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; +import org.apache.dolphinscheduler.spi.utils.JSONUtils; + +public class JavaTaskChannel implements TaskChannel { + + /** + * Cancel the mission + * + * @param status + * @return void + **/ + @Override + public void cancelApplication(boolean status) { + + } + + /** + * Create a task + * + * @param taskRequest This parameter is the Echternach of the mission + * @return JavaTask + **/ + @Override + public JavaTask createTask(TaskExecutionContext taskRequest) { + return new JavaTask(taskRequest); + } + + /** + * Parses Java task parameters + * + * @param parametersNode + * @return: org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters + **/ + @Override + public AbstractParameters parseParameters(ParametersNode parametersNode) { + return JSONUtils.parseObject(parametersNode.getTaskParams(), JavaParameters.class); + } + + /** + * Gets a list of the resources that the task depends on + * + * @param parameters + * @return ResourceParametersHelper + **/ + @Override + public ResourceParametersHelper getResources(String parameters) { + return null; + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannelFactory.java new file mode 100644 index 0000000000..c374960d45 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannelFactory.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.task.java; + +import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; +import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; +import org.apache.dolphinscheduler.spi.params.base.PluginParams; + +import java.util.List; + +import com.google.auto.service.AutoService; + +@AutoService(TaskChannelFactory.class) +public class JavaTaskChannelFactory implements TaskChannelFactory { + /** + * Construct a channel for a Java task + * + * @return TaskChannel + **/ + @Override + public TaskChannel create() { + return new JavaTaskChannel(); + } + + /** + * Get a unique identifier of the Java task + * + * @return String + **/ + @Override + public String getName() { + return "JAVA"; + } + + /** + * Gets the plug-in parameters for the Java task + * + * @return List + **/ + @Override + public List getParams() { + return null; + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/exception/JavaSourceFileExistException.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/exception/JavaSourceFileExistException.java new file mode 100644 index 0000000000..421d77de66 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/exception/JavaSourceFileExistException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.task.java.exception; + +public class JavaSourceFileExistException extends RuntimeException { + public JavaSourceFileExistException() { + } + + public JavaSourceFileExistException(String message) { + super(message); + } + + public JavaSourceFileExistException(String message, Throwable cause) { + super(message, cause); + } + + public JavaSourceFileExistException(Throwable cause) { + super(cause); + } + + public JavaSourceFileExistException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/exception/PublicClassNotFoundException.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/exception/PublicClassNotFoundException.java new file mode 100644 index 0000000000..16b0070c97 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/exception/PublicClassNotFoundException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.task.java.exception; + +public class PublicClassNotFoundException extends RuntimeException { + public PublicClassNotFoundException() { + } + + public PublicClassNotFoundException(String message) { + super(message); + } + + public PublicClassNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public PublicClassNotFoundException(Throwable cause) { + super(cause); + } + + public PublicClassNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/exception/RunTypeNotFoundException.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/exception/RunTypeNotFoundException.java new file mode 100644 index 0000000000..d893690673 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/exception/RunTypeNotFoundException.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.task.java.exception; + +public class RunTypeNotFoundException extends RuntimeException { + + public RunTypeNotFoundException() { + super(); + } + + public RunTypeNotFoundException(String s) { + super(s); + } + + public RunTypeNotFoundException(String s, Throwable throwable) { + super(s, throwable); + } + + public RunTypeNotFoundException(Throwable throwable) { + super(throwable); + } + + protected RunTypeNotFoundException(String s, Throwable throwable, boolean b, boolean b1) { + super(s, throwable, b, b1); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/test/org/apache/dolphinscheduler/plugin/task/java/JavaTaskTest.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/test/org/apache/dolphinscheduler/plugin/task/java/JavaTaskTest.java new file mode 100644 index 0000000000..89cc4f89e5 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/test/org/apache/dolphinscheduler/plugin/task/java/JavaTaskTest.java @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.task.java; + +import static org.apache.dolphinscheduler.plugin.task.api.enums.DataType.VARCHAR; +import static org.apache.dolphinscheduler.plugin.task.api.enums.Direct.IN; +import static org.apache.dolphinscheduler.plugin.task.java.JavaConstants.RUN_TYPE_JAR; +import static org.apache.dolphinscheduler.plugin.task.java.JavaConstants.RUN_TYPE_JAVA; + +import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; +import org.apache.dolphinscheduler.plugin.task.api.model.Property; +import org.apache.dolphinscheduler.plugin.task.api.model.ResourceInfo; +import org.apache.dolphinscheduler.plugin.task.java.exception.JavaSourceFileExistException; +import org.apache.dolphinscheduler.plugin.task.java.exception.PublicClassNotFoundException; +import org.apache.dolphinscheduler.plugin.task.java.exception.RunTypeNotFoundException; +import org.apache.dolphinscheduler.spi.utils.JSONUtils; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; + +import org.junit.Assert; +import org.junit.Test; + +public class JavaTaskTest { + + @Test + public void testGetPubllicClassName(){ + JavaTask javaTask = runJavaType(); + Assert.assertEquals(javaTask.getPublicClassName("import java.io.IOException;\n" + + "public class JavaTaskTest {\n" + + " public static void main(String[] args) throws IOException {\n" + + " StringBuilder builder = new StringBuilder(\"Hello: \");\n" + + " for (String arg : args) {\n" + + " builder.append(arg).append(\" \");\n" + + " }\n" + + " System.out.println(builder);\n" + + " }\n" + + "}\n"), "JavaTaskTest"); + } + + /** + * Construct a java -jar command + * + * @return void + **/ + @Test + public void buildJarCommand() { + String homeBinPath = JavaConstants.JAVA_HOME_VAR + File.separator + "bin" + File.separator; + JavaTask javaTask = runJarType(); + Assert.assertEquals(javaTask.buildJarCommand(), homeBinPath + + "java --class-path .:/tmp/dolphinscheduler/test/executepath:/tmp/dolphinscheduler/test/executepath/opt/share/jar/resource2.jar -jar /tmp/dolphinscheduler/test/executepath/opt/share/jar/main.jar -host 127.0.0.1 -port 8080 -xms:50m"); + } + + /** + * Construct the compile command + * + * @return void + **/ + @Test + public void buildJavaCompileCommand() throws IOException { + JavaTask javaTask = runJavaType(); + String sourceCode = javaTask.buildJavaSourceContent(); + String publicClassName = javaTask.getPublicClassName(sourceCode); + Assert.assertEquals("JavaTaskTest", publicClassName); + String fileName = javaTask.buildJavaSourceCodeFileFullName(publicClassName); + try { + String homeBinPath = JavaConstants.JAVA_HOME_VAR + File.separator + "bin" + File.separator; + Path path = Paths.get(fileName); + if (Files.exists(path)) { + Files.delete(path); + } + Assert.assertEquals(homeBinPath + + "javac --class-path .:/tmp/dolphinscheduler/test/executepath:/tmp/dolphinscheduler/test/executepath/opt/share/jar/resource2.jar /tmp/dolphinscheduler/test/executepath/JavaTaskTest.java", + javaTask.buildJavaCompileCommand(sourceCode)); + } finally { + Path path = Paths.get(fileName); + if (Files.exists(path)) { + Files.delete(path); + } + } + + } + + /** + * Construct java to run the command + * + * @return void + **/ + @Test + public void buildJavaCommand() throws Exception { + String wantJavaCommand = "${JAVA_HOME}/bin/javac --class-path .:/tmp/dolphinscheduler/test/executepath:/tmp/dolphinscheduler/test/executepath/opt/share/jar/resource2.jar /tmp/dolphinscheduler/test/executepath/JavaTaskTest.java;${JAVA_HOME}/bin/java --class-path .:/tmp/dolphinscheduler/test/executepath:/tmp/dolphinscheduler/test/executepath/opt/share/jar/resource2.jar JavaTaskTest -host 127.0.0.1 -port 8080 -xms:50m"; + JavaTask javaTask = runJavaType(); + String sourceCode = javaTask.buildJavaSourceContent(); + String publicClassName = javaTask.getPublicClassName(sourceCode); + Assert.assertEquals("JavaTaskTest", publicClassName); + String fileName = javaTask.buildJavaSourceCodeFileFullName(publicClassName); + Path path = Paths.get(fileName); + if (Files.exists(path)) { + Files.delete(path); + } + Assert.assertEquals(wantJavaCommand, javaTask.buildJavaCommand()); + } + + /** + * There is no exception to overwriting the Java source file + * @return void + * @throws IOException + **/ + @Test(expected = JavaSourceFileExistException.class) + public void coverJavaSourceFileExistException() throws IOException { + JavaTask javaTask = runJavaType(); + String sourceCode = javaTask.buildJavaSourceContent(); + String publicClassName = javaTask.getPublicClassName(sourceCode); + Assert.assertEquals("JavaTaskTest", publicClassName); + String fileName = javaTask.buildJavaSourceCodeFileFullName(publicClassName); + try { + Path path = Paths.get(fileName); + if (!Files.exists(path)) { + Files.createDirectories(path); + } + javaTask.createJavaSourceFileIfNotExists(sourceCode,fileName); + } finally { + Path path = Paths.get(fileName); + if (Files.exists(path)) { + Files.delete(path); + } + } + } + + /** + * The override class name could not find an exception + * + * @return void + **/ + @Test(expected = PublicClassNotFoundException.class) + public void coverPublicClassNotFoundException() { + JavaTask javaTask = runJavaType(); + javaTask.getPublicClassName(""); + } + + /** + * The override run mode could not find an exception + * + * @return void + * @throws Exception + **/ + @Test(expected = RunTypeNotFoundException.class) + public void coverRunTypeNotFoundException() throws Exception { + JavaTask javaTask = runJavaType(); + Field javaParameters = JavaTask.class.getDeclaredField("javaParameters"); + javaParameters.setAccessible(true); + ((JavaParameters)(javaParameters.get(javaTask))).setRunType(""); + javaTask.handle(); + javaTask.getPublicClassName(""); + } + + /** + * Create a Java task parameter mock object + * + * @param runType + * @return JavaParameters + **/ + public JavaParameters createJavaParametersObject(String runType) { + JavaParameters javaParameters = new JavaParameters(); + javaParameters.setRunType(runType); + javaParameters.setModulePath(false); + javaParameters.setJvmArgs("-xms:50m"); + javaParameters.setMainArgs("-host 127.0.0.1 -port 8080"); + ResourceInfo resourceJar = new ResourceInfo(); + resourceJar.setId(2); + resourceJar.setResourceName("/opt/share/jar/resource2.jar"); + resourceJar.setRes("I'm resource2.jar"); + ArrayList resourceInfoArrayList = new ArrayList<>(); + resourceInfoArrayList.add(resourceJar); + javaParameters.setResourceList(resourceInfoArrayList); + javaParameters.setRawScript( + "import java.io.IOException;\n" + + "public class JavaTaskTest {\n" + + " public static void main(String[] args) throws IOException {\n" + + " StringBuilder builder = new StringBuilder(\"Hello: \");\n" + + " for (String arg : args) {\n" + + " builder.append(arg).append(\" \");\n" + + " }\n" + " System.out.println(builder);\n" + + " }\n" + + "}\n"); + ArrayList localParams = new ArrayList<>(); + Property property = new Property(); + property.setProp("name"); + property.setValue("zhangsan"); + property.setDirect(IN); + property.setType(VARCHAR); + javaParameters.setLocalParams(localParams); + ResourceInfo mainJar = new ResourceInfo(); + mainJar.setId(1); + mainJar.setResourceName("/opt/share/jar/main.jar"); + mainJar.setRes("I'm main.jar"); + javaParameters.setMainJar(mainJar); + return javaParameters; + } + + /** + * A Java task that constructs the Java runtime pattern + * + * @return JavaTask + **/ + public JavaTask runJavaType() { + TaskExecutionContext taskExecutionContext = new TaskExecutionContext(); + taskExecutionContext.setTaskParams(JSONUtils.toJsonString(createJavaParametersObject(RUN_TYPE_JAVA))); + taskExecutionContext.setExecutePath("/tmp/dolphinscheduler/test/executepath"); + taskExecutionContext.setTaskAppId("runJavaType"); + JavaTask javaTask = new JavaTask(taskExecutionContext); + javaTask.init(); + return javaTask; + } + + /** + * The Java task to construct the jar run mode + * + * @return JavaTask + **/ + public JavaTask runJarType() { + TaskExecutionContext taskExecutionContext = new TaskExecutionContext(); + taskExecutionContext.setTaskParams(JSONUtils.toJsonString(createJavaParametersObject(RUN_TYPE_JAR))); + taskExecutionContext.setExecutePath("/tmp/dolphinscheduler/test/executepath"); + taskExecutionContext.setTaskAppId("runJavaType"); + JavaTask javaTask = new JavaTask(taskExecutionContext); + javaTask.init(); + return javaTask; + } +} diff --git a/dolphinscheduler-task-plugin/pom.xml b/dolphinscheduler-task-plugin/pom.xml index 528e21f459..744a285826 100644 --- a/dolphinscheduler-task-plugin/pom.xml +++ b/dolphinscheduler-task-plugin/pom.xml @@ -56,6 +56,7 @@ dolphinscheduler-task-openmldb dolphinscheduler-task-dvc dolphinscheduler-task-dinky + dolphinscheduler-task-java dolphinscheduler-task-sagemaker dolphinscheduler-task-chunjun dolphinscheduler-task-flink-stream diff --git a/dolphinscheduler-ui/public/images/task-icons/java.png b/dolphinscheduler-ui/public/images/task-icons/java.png new file mode 100644 index 0000000000..218201404e Binary files /dev/null and b/dolphinscheduler-ui/public/images/task-icons/java.png differ diff --git a/dolphinscheduler-ui/public/images/task-icons/java_hover.png b/dolphinscheduler-ui/public/images/task-icons/java_hover.png new file mode 100644 index 0000000000..04eb74b399 Binary files /dev/null and b/dolphinscheduler-ui/public/images/task-icons/java_hover.png differ diff --git a/dolphinscheduler-ui/src/locales/en_US/project.ts b/dolphinscheduler-ui/src/locales/en_US/project.ts index fa599a466c..549e6092de 100644 --- a/dolphinscheduler-ui/src/locales/en_US/project.ts +++ b/dolphinscheduler-ui/src/locales/en_US/project.ts @@ -131,7 +131,6 @@ export default { switch_version: 'Switch To This Version', confirm_switch_version: 'Confirm Switch To This Version?', current_version: 'Current Version', - run_type: 'Run Type', scheduling_time: 'Scheduling Time', duration: 'Duration', run_times: 'Run Times', @@ -217,6 +216,7 @@ export default { workflow_state: 'Workflow State', version: 'Version', current_version: 'Current Version', + run_type: 'Run Type', switch_version: 'Switch To This Version', confirm_switch_version: 'Confirm Switch To This Version?', description: 'Description', @@ -309,6 +309,10 @@ export default { online: 'Online' }, node: { + jvm_args: 'Java VM Parameters', + jvm_args_tips: 'Please enter virtual machine parameters', + run_type: 'Run Type', + is_module_path: 'Use Module Path', return_back: 'Return', current_node_settings: 'Current node settings', instructions: 'Instructions', diff --git a/dolphinscheduler-ui/src/locales/zh_CN/project.ts b/dolphinscheduler-ui/src/locales/zh_CN/project.ts index a08d0dc678..ea019f24cc 100644 --- a/dolphinscheduler-ui/src/locales/zh_CN/project.ts +++ b/dolphinscheduler-ui/src/locales/zh_CN/project.ts @@ -310,6 +310,10 @@ export default { online: '已上线' }, node: { + is_module_path: '使用模块路径', + run_type: '运行类型', + jvm_args: '虚拟机参数', + jvm_args_tips: '请输入虚拟机参数', return_back: '返回上一节点', current_node_settings: '当前节点设置', instructions: '使用说明', diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/index.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/index.ts index d5235a9fbf..f37b79779d 100644 --- a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/index.ts +++ b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/index.ts @@ -49,6 +49,7 @@ export { useExecutorCores } from './use-executor-cores' export { useMainJar } from './use-main-jar' export { useResources } from './use-resources' export { useTaskDefinition } from './use-task-definition' +export { useJavaTaskMainJar } from './use-java-task-main-jar' export { useShell } from './use-shell' export { useSpark } from './use-spark' @@ -74,6 +75,7 @@ export { useOpenmldb } from './use-openmldb' export { useDvc } from './use-dvc' export { useDinky } from './use-dinky' export { useSagemaker } from './use-sagemaker' +export { useJava } from './use-java' export { useChunjun } from './use-chunjun' export { useChunjunDeployMode } from './use-chunjun-deploy-mode' export { usePytorch } from './use-pytorch' diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-java-task-main-jar.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-java-task-main-jar.ts new file mode 100644 index 0000000000..3fe1fea9ed --- /dev/null +++ b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-java-task-main-jar.ts @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { computed, ref, onMounted, watch } from 'vue' +import { useI18n } from 'vue-i18n' +import { queryResourceByProgramType } from '@/service/modules/resources' +import { useTaskNodeStore } from '@/store/project/task-node' +import utils from '@/utils' +import type { IJsonItem, ProgramType, IMainJar } from '../types' + +export function useJavaTaskMainJar(model: { [field: string]: any }): IJsonItem { + const { t } = useI18n() + const mainJarOptions = ref([] as IMainJar[]) + const taskStore = useTaskNodeStore() + + const mainJarSpan = computed(() => (model.runType === 'JAVA' ? 0 : 24)) + const getMainJars = async (programType: ProgramType) => { + const storeMainJar = taskStore.getMainJar(programType) + if (storeMainJar) { + mainJarOptions.value = storeMainJar + return + } + const res = await queryResourceByProgramType({ + type: 'FILE', + programType + }) + utils.removeUselessChildren(res) + mainJarOptions.value = res || [] + taskStore.updateMainJar(programType, res) + } + + onMounted(() => { + getMainJars(model.programType) + }) + + watch( + () => model.programType, + (value) => { + getMainJars(value) + } + ) + + return { + type: 'tree-select', + field: 'mainJar', + name: t('project.node.main_package'), + span: mainJarSpan, + props: { + cascade: true, + showPath: true, + checkStrategy: 'child', + placeholder: t('project.node.main_package_tips'), + keyField: 'id', + labelField: 'fullName' + }, + validate: { + trigger: ['input', 'blur'], + required: true, + validator(validate: any, value: string) { + if (!value) { + return new Error(t('project.node.main_package_tips')) + } + } + }, + options: mainJarOptions + } +} diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-java.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-java.ts new file mode 100644 index 0000000000..3b05a9c16d --- /dev/null +++ b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-java.ts @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { computed } from 'vue' +import { useI18n } from 'vue-i18n' +import { useCustomParams, useResources ,useJavaTaskMainJar} from '.' +import type { IJsonItem } from '../types' + +export function useJava(model: { [field: string]: any }): IJsonItem[] { + const { t } = useI18n() + const rawScriptSpan = computed(() => (model.runType === 'JAR' ? 0 : 24)) + return [ + { + type: 'select', + field: 'runType', + span: 12, + name: t('project.node.run_type'), + options: RUN_TYPES, + value: model.runType + }, + { + type: 'switch', + field: 'isModulePath', + span: 24, + name: t('project.node.is_module_path'), + value: model.isModulePath + }, + { + type: 'input', + field: 'mainArgs', + name: t('project.node.main_arguments'), + props: { + type: 'textarea', + placeholder: t('project.node.main_arguments_tips') + } + }, + { + type: 'input', + field: 'jvmArgs', + name: t('project.node.jvm_args'), + props: { + type: 'textarea', + placeholder: t('project.node.jvm_args_tips') + } + }, + useJavaTaskMainJar(model), + { + type: 'editor', + field: 'rawScript', + span: rawScriptSpan, + name: t('project.node.script'), + validate: { + trigger: ['input', 'trigger'], + required: true, + message: t('project.node.script_tips') + } + }, + useResources(), + ...useCustomParams({ model, field: 'localParams', isSimple: false }) + ] +} + +export const RUN_TYPES = [ + { + label: 'JAVA', + value: 'JAVA' + }, + { + label: 'JAR', + value: 'JAR' + } + ] + \ No newline at end of file diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/format-data.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/format-data.ts index 0d845fafc1..8c3a1497fc 100644 --- a/dolphinscheduler-ui/src/views/projects/task/components/node/format-data.ts +++ b/dolphinscheduler-ui/src/views/projects/task/components/node/format-data.ts @@ -35,6 +35,17 @@ export function formatParams(data: INodeData): { if (data.taskType === 'SUB_PROCESS') { taskParams.processDefinitionCode = data.processDefinitionCode } + + if(data.taskType === 'JAVA'){ + taskParams.runType = data.runType + taskParams.mainArgs = data.mainArgs + taskParams.jvmArgs = data.jvmArgs + taskParams.isModulePath = data.isModulePath + if(data.runType === 'JAR' && data.mainJar){ + taskParams.mainJar = {id: data.mainJar}; + } + } + if ( data.taskType && ['SPARK', 'MR', 'FLINK', 'FLINK_STREAM'].includes(data.taskType) diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/index.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/index.ts index 1217de2aeb..4ae4d5b14a 100644 --- a/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/index.ts +++ b/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/index.ts @@ -40,6 +40,7 @@ import { useJupyter } from './use-jupyter' import { useMlflow } from './use-mlflow' import { useOpenmldb } from './use-openmldb' import { useDvc } from './use-dvc' +import { useJava } from './use-java' import { useDinky } from './use-dinky' import { userSagemaker } from './use-sagemaker' import { useChunjun } from './use-chunjun' @@ -75,6 +76,7 @@ export default { SAGEMAKER: userSagemaker, CHUNJUN: useChunjun, FLINK_STREAM: useFlinkStream, + JAVA: useJava, PYTORCH: usePytorch, HIVECLI: useHiveCli } diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/use-java.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/use-java.ts new file mode 100644 index 0000000000..822ec193cc --- /dev/null +++ b/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/use-java.ts @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { reactive } from 'vue' +import * as Fields from '../fields/index' +import type { IJsonItem, INodeData } from '../types' +import { ITaskData } from '../types' + +export function useJava({ + projectCode, + from = 0, + readonly, + data +}: { + projectCode: number + from?: number + readonly?: boolean + data?: ITaskData +}) { + const model = reactive({ + name: '', + taskType: 'JAVA', + flag: 'YES', + description: '', + localParams: [], + environmentCode: null, + failRetryInterval: 1, + failRetryTimes: 0, + workerGroup: 'default', + delayTime: 0, + isModulePath: false, + rawScript: '', + timeoutFlag: false, + timeoutNotifyStrategy: ['WARN'], + timeout: 30, + mainJar: undefined, + runType:'JAVA', + mainArgs:'', + jvmArgs:'', + programType: 'JAVA' + } as unknown as INodeData) + + let extra: IJsonItem[] = [] + if (from === 1) { + extra = [ + Fields.useTaskType(model, readonly), + Fields.useProcessName({ + model, + projectCode, + isCreate: !data?.id, + from, + processName: data?.processName + }) + ] + } + + return { + json: [ + Fields.useName(from), + ...extra, + Fields.useRunFlag(), + Fields.useDescription(), + Fields.useTaskPriority(), + Fields.useWorkerGroup(), + Fields.useEnvironmentName(model, !model.id), + ...Fields.useTaskGroup(model, projectCode), + ...Fields.useFailed(), + Fields.useDelayTime(model), + ...Fields.useTimeoutAlarm(model), + ...Fields.useJava(model), + Fields.usePreTasks() + ] as IJsonItem[], + model + } +} diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/types.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/types.ts index 2dba623ea6..2d3451da08 100644 --- a/dolphinscheduler-ui/src/views/projects/task/components/node/types.ts +++ b/dolphinscheduler-ui/src/views/projects/task/components/node/types.ts @@ -223,6 +223,9 @@ interface ITaskParams { resourceList?: ISourceItem[] mainJar?: ISourceItem localParams?: ILocalParam[] + runType?:string + jvmArgs?:string + isModulePath?:boolean rawScript?: string initScript?: string programType?: string diff --git a/dolphinscheduler-ui/src/views/projects/task/constants/task-type.ts b/dolphinscheduler-ui/src/views/projects/task/constants/task-type.ts index 0ecc53bccc..d69f38788f 100644 --- a/dolphinscheduler-ui/src/views/projects/task/constants/task-type.ts +++ b/dolphinscheduler-ui/src/views/projects/task/constants/task-type.ts @@ -39,6 +39,7 @@ export type TaskType = | 'MLFLOW' | 'OPENMLDB' | 'DVC' + | 'JAVA' | 'DINKY' | 'SAGEMAKER' | 'CHUNJUN' @@ -49,6 +50,9 @@ export type TaskType = export type TaskExecuteType = 'STREAM' | 'BATCH' export const TASK_TYPES_MAP = { + JAVA: { + alias: 'JAVA' + }, SHELL: { alias: 'SHELL' }, diff --git a/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag.module.scss b/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag.module.scss index d8b4a3da28..8c27b71785 100644 --- a/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag.module.scss +++ b/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag.module.scss @@ -177,6 +177,9 @@ $bgLight: #ffffff; &.icon-dvc { background-image: url('/images/task-icons/dvc.png'); } + &.icon-java { + background-image: url('/images/task-icons/java.png'); + } &.icon-dinky { background-image: url('/images/task-icons/dinky.png'); } @@ -272,6 +275,9 @@ $bgLight: #ffffff; &.icon-dinky { background-image: url('/images/task-icons/dinky_hover.png'); } + &.icon-java { + background-image: url('/images/task-icons/java.png'); + } &.icon-sagemaker { background-image: url('/images/task-icons/sagemaker_hover.png'); }