11 changed files with 11 additions and 656 deletions
@ -1,29 +0,0 @@
|
||||
/* |
||||
* 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.alert.script; |
||||
|
||||
public final class OSUtils { |
||||
|
||||
private OSUtils() { |
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); |
||||
} |
||||
|
||||
static Boolean isWindows() { |
||||
return System.getProperty("os.name").startsWith("Windows"); |
||||
} |
||||
} |
@ -1,361 +0,0 @@
|
||||
/* |
||||
* 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.api; |
||||
|
||||
import java.io.BufferedReader; |
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.io.InputStreamReader; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.Timer; |
||||
import java.util.TimerTask; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
/** |
||||
* A base class for running a Unix command. |
||||
* |
||||
* <code>AbstractShell</code> can be used to run unix commands like <code>du</code> or |
||||
* <code>df</code>. It also offers facilities to gate commands by |
||||
* time-intervals. |
||||
*/ |
||||
@Slf4j |
||||
public abstract class AbstractShell { |
||||
|
||||
/** |
||||
* Time after which the executing script would be timedout |
||||
*/ |
||||
protected long timeOutInterval = 0L; |
||||
/** |
||||
* If or not script timed out |
||||
*/ |
||||
private AtomicBoolean timedOut; |
||||
|
||||
/** |
||||
* refresh interval in msec |
||||
*/ |
||||
private long interval; |
||||
|
||||
/** |
||||
* last time the command was performed |
||||
*/ |
||||
private long lastTime; |
||||
|
||||
/** |
||||
* env for the command execution |
||||
*/ |
||||
private Map<String, String> environment; |
||||
|
||||
private File dir; |
||||
|
||||
/** |
||||
* sub process used to execute the command |
||||
*/ |
||||
private Process process; |
||||
private int exitCode; |
||||
|
||||
/** |
||||
* If or not script finished executing |
||||
*/ |
||||
private AtomicBoolean completed; |
||||
|
||||
AbstractShell() { |
||||
this(0L); |
||||
} |
||||
|
||||
/** |
||||
* @param interval the minimum duration to wait before re-executing the |
||||
* command. |
||||
*/ |
||||
AbstractShell(long interval) { |
||||
this.interval = interval; |
||||
this.lastTime = (interval < 0) ? 0 : -interval; |
||||
} |
||||
|
||||
/** |
||||
* set the environment for the command |
||||
* |
||||
* @param env Mapping of environment variables |
||||
*/ |
||||
protected void setEnvironment(Map<String, String> env) { |
||||
this.environment = env; |
||||
} |
||||
|
||||
/** |
||||
* set the working directory |
||||
* |
||||
* @param dir The directory where the command would be executed |
||||
*/ |
||||
protected void setWorkingDirectory(File dir) { |
||||
this.dir = dir; |
||||
} |
||||
|
||||
/** |
||||
* check to see if a command needs to be executed and execute if needed |
||||
* |
||||
* @throws IOException errors |
||||
*/ |
||||
protected void run() throws IOException { |
||||
if (lastTime + interval > System.currentTimeMillis()) { |
||||
return; |
||||
} |
||||
// reset for next run
|
||||
exitCode = 0; |
||||
runCommand(); |
||||
} |
||||
|
||||
/** |
||||
* Run a command actual work |
||||
*/ |
||||
private void runCommand() throws IOException { |
||||
ProcessBuilder builder = new ProcessBuilder(getExecString()); |
||||
Timer timeOutTimer = null; |
||||
ShellTimeoutTimerTask timeoutTimerTask; |
||||
timedOut = new AtomicBoolean(false); |
||||
completed = new AtomicBoolean(false); |
||||
|
||||
if (environment != null) { |
||||
builder.environment().putAll(this.environment); |
||||
} |
||||
if (dir != null) { |
||||
builder.directory(this.dir); |
||||
} |
||||
|
||||
process = builder.start(); |
||||
ProcessContainer.putProcess(process); |
||||
|
||||
if (timeOutInterval > 0) { |
||||
timeOutTimer = new Timer(); |
||||
timeoutTimerTask = new ShellTimeoutTimerTask( |
||||
this); |
||||
// One time scheduling.
|
||||
timeOutTimer.schedule(timeoutTimerTask, timeOutInterval); |
||||
} |
||||
final BufferedReader errReader = |
||||
new BufferedReader( |
||||
new InputStreamReader(process.getErrorStream())); |
||||
BufferedReader inReader = |
||||
new BufferedReader( |
||||
new InputStreamReader(process.getInputStream())); |
||||
final StringBuilder errMsg = new StringBuilder(); |
||||
|
||||
// read error and input streams as this would free up the buffers
|
||||
// free the error stream buffer
|
||||
Thread errThread = new Thread() { |
||||
|
||||
@Override |
||||
public void run() { |
||||
try { |
||||
String line = errReader.readLine(); |
||||
while ((line != null) && !isInterrupted()) { |
||||
errMsg.append(line); |
||||
errMsg.append(System.getProperty("line.separator")); |
||||
line = errReader.readLine(); |
||||
} |
||||
} catch (IOException ioe) { |
||||
log.warn("Error reading the error stream", ioe); |
||||
} |
||||
} |
||||
}; |
||||
Thread inThread = new Thread() { |
||||
|
||||
@Override |
||||
public void run() { |
||||
try { |
||||
parseExecResult(inReader); |
||||
} catch (IOException ioe) { |
||||
log.warn("Error reading the in stream", ioe); |
||||
} |
||||
super.run(); |
||||
} |
||||
}; |
||||
try { |
||||
errThread.start(); |
||||
inThread.start(); |
||||
} catch (IllegalStateException e) { |
||||
log.error(" read error and input streams start error", e); |
||||
} |
||||
try { |
||||
// parse the output
|
||||
exitCode = process.waitFor(); |
||||
try { |
||||
// make sure that the error and in thread exits
|
||||
errThread.join(); |
||||
inThread.join(); |
||||
} catch (InterruptedException ie) { |
||||
log.warn("Interrupted while reading the error and in stream", ie); |
||||
} |
||||
completed.compareAndSet(false, true); |
||||
// the timeout thread handling
|
||||
// taken care in finally block
|
||||
if (exitCode != 0 || errMsg.length() > 0) { |
||||
throw new ExitCodeException(exitCode, errMsg.toString()); |
||||
} |
||||
} catch (InterruptedException ie) { |
||||
throw new IOException(ie.toString()); |
||||
} finally { |
||||
if ((timeOutTimer != null) && !timedOut.get()) { |
||||
timeOutTimer.cancel(); |
||||
} |
||||
// close the input stream
|
||||
try { |
||||
inReader.close(); |
||||
} catch (IOException ioe) { |
||||
log.warn("Error while closing the input stream", ioe); |
||||
} |
||||
if (!completed.get()) { |
||||
errThread.interrupt(); |
||||
} |
||||
try { |
||||
errReader.close(); |
||||
} catch (IOException ioe) { |
||||
log.warn("Error while closing the error stream", ioe); |
||||
} |
||||
ProcessContainer.removeProcess(process); |
||||
process.destroy(); |
||||
lastTime = System.currentTimeMillis(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @return an array containing the command name and its parameters |
||||
*/ |
||||
protected abstract String[] getExecString(); |
||||
|
||||
/** |
||||
* Parse the execution result |
||||
* |
||||
* @param lines lines |
||||
* @throws IOException errors |
||||
*/ |
||||
protected abstract void parseExecResult(BufferedReader lines) throws IOException; |
||||
|
||||
/** |
||||
* get the current sub-process executing the given command |
||||
* |
||||
* @return process executing the command |
||||
*/ |
||||
public Process getProcess() { |
||||
return process; |
||||
} |
||||
|
||||
/** |
||||
* get the exit code |
||||
* |
||||
* @return the exit code of the process |
||||
*/ |
||||
public int getExitCode() { |
||||
return exitCode; |
||||
} |
||||
|
||||
/** |
||||
* Set if the command has timed out. |
||||
*/ |
||||
private void setTimedOut() { |
||||
this.timedOut.set(true); |
||||
} |
||||
|
||||
/** |
||||
* Timer which is used to timeout scripts spawned off by shell. |
||||
*/ |
||||
private static class ShellTimeoutTimerTask extends TimerTask { |
||||
|
||||
private AbstractShell shell; |
||||
|
||||
public ShellTimeoutTimerTask(AbstractShell shell) { |
||||
this.shell = shell; |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
Process p = shell.getProcess(); |
||||
try { |
||||
p.exitValue(); |
||||
} catch (Exception e) { |
||||
// Process has not terminated.
|
||||
// So check if it has completed
|
||||
// if not just destroy it.
|
||||
if (p != null && !shell.completed.get()) { |
||||
shell.setTimedOut(); |
||||
p.destroy(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This is an IOException with exit code added. |
||||
*/ |
||||
public static class ExitCodeException extends IOException { |
||||
|
||||
private final int exitCode; |
||||
|
||||
public ExitCodeException(int exitCode, String message) { |
||||
super(message); |
||||
this.exitCode = exitCode; |
||||
} |
||||
|
||||
public int getExitCode() { |
||||
return exitCode; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* process manage container |
||||
*/ |
||||
public static class ProcessContainer extends ConcurrentHashMap<Integer, Process> { |
||||
|
||||
private static final ProcessContainer container = new ProcessContainer(); |
||||
|
||||
private ProcessContainer() { |
||||
super(); |
||||
} |
||||
|
||||
public static final ProcessContainer getInstance() { |
||||
return container; |
||||
} |
||||
|
||||
public static void putProcess(Process process) { |
||||
getInstance().put(process.hashCode(), process); |
||||
} |
||||
|
||||
public static int processSize() { |
||||
return getInstance().size(); |
||||
} |
||||
|
||||
public static void removeProcess(Process process) { |
||||
getInstance().remove(process.hashCode()); |
||||
} |
||||
|
||||
public static void destroyAllProcess() { |
||||
Set<Entry<Integer, Process>> set = getInstance().entrySet(); |
||||
for (Entry<Integer, Process> entry : set) { |
||||
try { |
||||
entry.getValue().destroy(); |
||||
} catch (Exception e) { |
||||
log.error("Destroy All Processes error", e); |
||||
} |
||||
} |
||||
|
||||
log.info("close " + set.size() + " executing process tasks"); |
||||
} |
||||
} |
||||
} |
@ -1,177 +0,0 @@
|
||||
/* |
||||
* 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.api; |
||||
|
||||
import java.io.BufferedReader; |
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* shell command executor. |
||||
* |
||||
* <code>ShellExecutor</code> should be used in cases where the output |
||||
* of the command needs no explicit parsing and where the command, working |
||||
* directory and the environment remains unchanged. The output of the command |
||||
* is stored as-is and is expected to be small. |
||||
*/ |
||||
public class ShellExecutor extends AbstractShell { |
||||
|
||||
private String[] command; |
||||
private StringBuilder output; |
||||
|
||||
public ShellExecutor(String... execString) { |
||||
this(execString, null); |
||||
} |
||||
|
||||
public ShellExecutor(String[] execString, File dir) { |
||||
this(execString, dir, null); |
||||
} |
||||
|
||||
public ShellExecutor(String[] execString, File dir, |
||||
Map<String, String> env) { |
||||
this(execString, dir, env, 0L); |
||||
} |
||||
|
||||
/** |
||||
* Create a new instance of the ShellExecutor to execute a command. |
||||
* |
||||
* @param execString The command to execute with arguments |
||||
* @param dir If not-null, specifies the directory which should be set |
||||
* as the current working directory for the command. |
||||
* If null, the current working directory is not modified. |
||||
* @param env If not-null, environment of the command will include the |
||||
* key-value pairs specified in the map. If null, the current |
||||
* environment is not modified. |
||||
* @param timeout Specifies the time in milliseconds, after which the |
||||
* command will be killed and the status marked as timedout. |
||||
* If 0, the command will not be timed out. |
||||
*/ |
||||
public ShellExecutor(String[] execString, File dir, |
||||
Map<String, String> env, long timeout) { |
||||
command = execString.clone(); |
||||
if (dir != null) { |
||||
setWorkingDirectory(dir); |
||||
} |
||||
if (env != null) { |
||||
setEnvironment(env); |
||||
} |
||||
timeOutInterval = timeout; |
||||
} |
||||
|
||||
/** |
||||
* Static method to execute a shell command. |
||||
* Covers most of the simple cases without requiring the user to implement |
||||
* the <code>AbstractShell</code> interface. |
||||
* |
||||
* @param cmd shell command to execute. |
||||
* @return the output of the executed command. |
||||
* @throws IOException errors |
||||
*/ |
||||
public static String execCommand(String... cmd) throws IOException { |
||||
return execCommand(null, cmd, 0L); |
||||
} |
||||
|
||||
/** |
||||
* Static method to execute a shell command. |
||||
* Covers most of the simple cases without requiring the user to implement |
||||
* the <code>AbstractShell</code> interface. |
||||
* |
||||
* @param env the map of environment key=value |
||||
* @param cmd shell command to execute. |
||||
* @param timeout time in milliseconds after which script should be marked timeout |
||||
* @return the output of the executed command. |
||||
* @throws IOException errors |
||||
*/ |
||||
public static String execCommand(Map<String, String> env, String[] cmd, |
||||
long timeout) throws IOException { |
||||
ShellExecutor exec = new ShellExecutor(cmd, null, env, |
||||
timeout); |
||||
exec.execute(); |
||||
return exec.getOutput(); |
||||
} |
||||
|
||||
/** |
||||
* Static method to execute a shell command. |
||||
* Covers most of the simple cases without requiring the user to implement |
||||
* the <code>AbstractShell</code> interface. |
||||
* |
||||
* @param env the map of environment key=value |
||||
* @param cmd shell command to execute. |
||||
* @return the output of the executed command. |
||||
* @throws IOException errors |
||||
*/ |
||||
public static String execCommand(Map<String, String> env, String... cmd) throws IOException { |
||||
return execCommand(env, cmd, 0L); |
||||
} |
||||
|
||||
/** |
||||
* Execute the shell command |
||||
* |
||||
* @throws IOException errors |
||||
*/ |
||||
public void execute() throws IOException { |
||||
this.run(); |
||||
} |
||||
|
||||
@Override |
||||
protected String[] getExecString() { |
||||
return command; |
||||
} |
||||
|
||||
@Override |
||||
protected void parseExecResult(BufferedReader lines) throws IOException { |
||||
output = new StringBuilder(); |
||||
char[] buf = new char[1024]; |
||||
int nRead; |
||||
String line = ""; |
||||
while ((nRead = lines.read(buf, 0, buf.length)) > 0) { |
||||
line = new String(buf, 0, nRead); |
||||
output.append(line); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @return the output of the shell command |
||||
*/ |
||||
public String getOutput() { |
||||
return (output == null) ? "" : output.toString(); |
||||
} |
||||
|
||||
/** |
||||
* Returns the commands of this instance. |
||||
* Arguments with spaces in are presented with quotes round; other |
||||
* arguments are presented raw |
||||
* |
||||
* @return a string representation of the object |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
StringBuilder builder = new StringBuilder(); |
||||
String[] args = getExecString(); |
||||
for (String s : args) { |
||||
if (s.indexOf(' ') >= 0) { |
||||
builder.append('"').append(s).append('"'); |
||||
} else { |
||||
builder.append(s); |
||||
} |
||||
builder.append(' '); |
||||
} |
||||
return builder.toString(); |
||||
} |
||||
} |
@ -1,84 +0,0 @@
|
||||
/* |
||||
* 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.api.utils; |
||||
|
||||
import org.apache.dolphinscheduler.common.utils.PropertyUtils; |
||||
import org.apache.dolphinscheduler.plugin.task.api.ShellExecutor; |
||||
|
||||
import org.apache.commons.lang3.StringUtils; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.StringTokenizer; |
||||
|
||||
public class OSUtils { |
||||
|
||||
private OSUtils() { |
||||
throw new IllegalStateException("Utility class"); |
||||
} |
||||
|
||||
/** |
||||
* get sudo command |
||||
* |
||||
* @param tenantCode tenantCode |
||||
* @param command command |
||||
* @return result of sudo execute command |
||||
*/ |
||||
public static String getSudoCmd(String tenantCode, String command) { |
||||
return StringUtils.isEmpty(tenantCode) || !isSudoEnable() ? command : "sudo -u " + tenantCode + " " + command; |
||||
} |
||||
|
||||
/** |
||||
* use sudo or not |
||||
* |
||||
* @return true is use sudo |
||||
*/ |
||||
public static boolean isSudoEnable() { |
||||
return PropertyUtils.getBoolean("sudo.enable", Boolean.TRUE); |
||||
} |
||||
|
||||
/** |
||||
* Execute the corresponding command of Linux or Windows |
||||
* |
||||
* @param command command |
||||
* @return result of execute command |
||||
* @throws IOException errors |
||||
*/ |
||||
public static String exeCmd(String command) throws IOException { |
||||
StringTokenizer st = new StringTokenizer(command); |
||||
String[] cmdArray = new String[st.countTokens()]; |
||||
for (int i = 0; st.hasMoreTokens(); i++) { |
||||
cmdArray[i] = st.nextToken(); |
||||
} |
||||
return exeShell(cmdArray); |
||||
} |
||||
|
||||
/** |
||||
* Execute the shell |
||||
* |
||||
* @param command command |
||||
* @return result of execute the shell |
||||
* @throws IOException errors |
||||
*/ |
||||
public static String exeShell(String[] command) throws IOException { |
||||
return ShellExecutor.execCommand(command); |
||||
} |
||||
|
||||
public static String getOSName() { |
||||
return System.getProperty("os.name"); |
||||
} |
||||
} |
Loading…
Reference in new issue