Browse Source

access field handle of FileDescriptor in ProcessImplForWin32 by reflection for portability (#2113)

* access field handle of FileDescriptor in ProcessImplForWin32 by reflection for portability

Current implementation relies on `sun.misc.JavaIOFileDescriptorAccess`
which is only accessible on oraclejdk8.

Basically the demand is getting & setting `handle` field of
`FileDescriptor`, so we can directly do that with reflection.

Though, I suspect the necessity we introduce ProcessImplForWin32. Maybe
we could have a better way to support worker server to run bat script.

* harden initialization of ProcessImplForWin32

* ignore ShellTaskTest#testHandleForWindows outside Windows
pull/2/head
tison 5 years ago committed by GitHub
parent
commit
9224b49b58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/OSUtils.java
  2. 57
      dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/process/ProcessImplForWin32.java
  3. 3
      dolphinscheduler-server/src/test/java/org/apache/dolphinscheduler/server/worker/task/shell/ShellTaskTest.java

16
dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/OSUtils.java

@ -400,8 +400,7 @@ public class OSUtils {
* @return true if mac * @return true if mac
*/ */
public static boolean isMacOS() { public static boolean isMacOS() {
String os = System.getProperty("os.name"); return getOSName().startsWith("Mac");
return os.startsWith("Mac");
} }
@ -409,9 +408,16 @@ public class OSUtils {
* whether is windows * whether is windows
* @return true if windows * @return true if windows
*/ */
public static boolean isWindows() { public static boolean isWindows() { ;
String os = System.getProperty("os.name"); return getOSName().startsWith("Windows");
return os.startsWith("Windows"); }
/**
* get current OS name
* @return current OS name
*/
public static String getOSName() {
return System.getProperty("os.name");
} }
/** /**

57
dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/process/ProcessImplForWin32.java

@ -19,6 +19,8 @@ package org.apache.dolphinscheduler.common.utils.process;
import com.sun.jna.Pointer; import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.*; import com.sun.jna.platform.win32.*;
import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.IntByReference;
import java.lang.reflect.Field;
import org.apache.dolphinscheduler.common.utils.OSUtils;
import sun.security.action.GetPropertyAction; import sun.security.action.GetPropertyAction;
import java.io.*; import java.io.*;
@ -31,10 +33,25 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static com.sun.jna.platform.win32.WinBase.STILL_ACTIVE; import static com.sun.jna.platform.win32.WinBase.STILL_ACTIVE;
import static java.util.Objects.requireNonNull;
public class ProcessImplForWin32 extends Process { public class ProcessImplForWin32 extends Process {
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess(); private static final Field FD_HANDLE;
static {
if (!OSUtils.isWindows()) {
throw new RuntimeException("ProcessImplForWin32 can be only initialized in " +
"Windows environment, but current OS is " + OSUtils.getOSName());
}
try {
FD_HANDLE = requireNonNull(FileDescriptor.class.getDeclaredField("handle"));
FD_HANDLE.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
private static final int PIPE_SIZE = 4096 + 24; private static final int PIPE_SIZE = 4096 + 24;
@ -46,6 +63,22 @@ public class ProcessImplForWin32 extends Process {
private static final WinNT.HANDLE JAVA_INVALID_HANDLE_VALUE = new WinNT.HANDLE(Pointer.createConstant(-1)); private static final WinNT.HANDLE JAVA_INVALID_HANDLE_VALUE = new WinNT.HANDLE(Pointer.createConstant(-1));
private static void setHandle(FileDescriptor obj, long handle) {
try {
FD_HANDLE.set(obj, handle);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private static long getHandle(FileDescriptor obj) {
try {
return (Long) FD_HANDLE.get(obj);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/** /**
* Open a file for writing. If {@code append} is {@code true} then the file * Open a file for writing. If {@code append} is {@code true} then the file
* is opened for atomic append directly and a FileOutputStream constructed * is opened for atomic append directly and a FileOutputStream constructed
@ -63,7 +96,7 @@ public class ProcessImplForWin32 extends Process {
sm.checkWrite(path); sm.checkWrite(path);
long handle = openForAtomicAppend(path); long handle = openForAtomicAppend(path);
final FileDescriptor fd = new FileDescriptor(); final FileDescriptor fd = new FileDescriptor();
fdAccess.setHandle(fd, handle); setHandle(fd, handle);
return AccessController.doPrivileged( return AccessController.doPrivileged(
new PrivilegedAction<FileOutputStream>() { new PrivilegedAction<FileOutputStream>() {
public FileOutputStream run() { public FileOutputStream run() {
@ -102,30 +135,30 @@ public class ProcessImplForWin32 extends Process {
if (redirects[0] == ProcessBuilderForWin32.Redirect.PIPE) if (redirects[0] == ProcessBuilderForWin32.Redirect.PIPE)
stdHandles[0] = -1L; stdHandles[0] = -1L;
else if (redirects[0] == ProcessBuilderForWin32.Redirect.INHERIT) else if (redirects[0] == ProcessBuilderForWin32.Redirect.INHERIT)
stdHandles[0] = fdAccess.getHandle(FileDescriptor.in); stdHandles[0] = getHandle(FileDescriptor.in);
else { else {
f0 = new FileInputStream(redirects[0].file()); f0 = new FileInputStream(redirects[0].file());
stdHandles[0] = fdAccess.getHandle(f0.getFD()); stdHandles[0] = getHandle(f0.getFD());
} }
if (redirects[1] == ProcessBuilderForWin32.Redirect.PIPE) if (redirects[1] == ProcessBuilderForWin32.Redirect.PIPE)
stdHandles[1] = -1L; stdHandles[1] = -1L;
else if (redirects[1] == ProcessBuilderForWin32.Redirect.INHERIT) else if (redirects[1] == ProcessBuilderForWin32.Redirect.INHERIT)
stdHandles[1] = fdAccess.getHandle(FileDescriptor.out); stdHandles[1] = getHandle(FileDescriptor.out);
else { else {
f1 = newFileOutputStream(redirects[1].file(), f1 = newFileOutputStream(redirects[1].file(),
redirects[1].append()); redirects[1].append());
stdHandles[1] = fdAccess.getHandle(f1.getFD()); stdHandles[1] = getHandle(f1.getFD());
} }
if (redirects[2] == ProcessBuilderForWin32.Redirect.PIPE) if (redirects[2] == ProcessBuilderForWin32.Redirect.PIPE)
stdHandles[2] = -1L; stdHandles[2] = -1L;
else if (redirects[2] == ProcessBuilderForWin32.Redirect.INHERIT) else if (redirects[2] == ProcessBuilderForWin32.Redirect.INHERIT)
stdHandles[2] = fdAccess.getHandle(FileDescriptor.err); stdHandles[2] = getHandle(FileDescriptor.err);
else { else {
f2 = newFileOutputStream(redirects[2].file(), f2 = newFileOutputStream(redirects[2].file(),
redirects[2].append()); redirects[2].append());
stdHandles[2] = fdAccess.getHandle(f2.getFD()); stdHandles[2] = getHandle(f2.getFD());
} }
} }
@ -442,7 +475,7 @@ public class ProcessImplForWin32 extends Process {
stdin_stream = ProcessBuilderForWin32.NullOutputStream.INSTANCE; stdin_stream = ProcessBuilderForWin32.NullOutputStream.INSTANCE;
else { else {
FileDescriptor stdin_fd = new FileDescriptor(); FileDescriptor stdin_fd = new FileDescriptor();
fdAccess.setHandle(stdin_fd, stdHandles[0]); setHandle(stdin_fd, stdHandles[0]);
stdin_stream = new BufferedOutputStream( stdin_stream = new BufferedOutputStream(
new FileOutputStream(stdin_fd)); new FileOutputStream(stdin_fd));
} }
@ -451,7 +484,7 @@ public class ProcessImplForWin32 extends Process {
stdout_stream = ProcessBuilderForWin32.NullInputStream.INSTANCE; stdout_stream = ProcessBuilderForWin32.NullInputStream.INSTANCE;
else { else {
FileDescriptor stdout_fd = new FileDescriptor(); FileDescriptor stdout_fd = new FileDescriptor();
fdAccess.setHandle(stdout_fd, stdHandles[1]); setHandle(stdout_fd, stdHandles[1]);
stdout_stream = new BufferedInputStream( stdout_stream = new BufferedInputStream(
new FileInputStream(stdout_fd)); new FileInputStream(stdout_fd));
} }
@ -460,7 +493,7 @@ public class ProcessImplForWin32 extends Process {
stderr_stream = ProcessBuilderForWin32.NullInputStream.INSTANCE; stderr_stream = ProcessBuilderForWin32.NullInputStream.INSTANCE;
else { else {
FileDescriptor stderr_fd = new FileDescriptor(); FileDescriptor stderr_fd = new FileDescriptor();
fdAccess.setHandle(stderr_fd, stdHandles[2]); setHandle(stderr_fd, stdHandles[2]);
stderr_stream = new FileInputStream(stderr_fd); stderr_stream = new FileInputStream(stderr_fd);
} }

3
dolphinscheduler-server/src/test/java/org/apache/dolphinscheduler/server/worker/task/shell/ShellTaskTest.java

@ -27,6 +27,7 @@ import org.apache.dolphinscheduler.service.bean.SpringApplicationContext;
import org.apache.dolphinscheduler.service.process.ProcessService; import org.apache.dolphinscheduler.service.process.ProcessService;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -172,7 +173,7 @@ public class ShellTaskTest {
@Test @Test
public void testHandleForWindows() throws Exception { public void testHandleForWindows() throws Exception {
try { try {
PowerMockito.when(OSUtils.isWindows()).thenReturn(true); Assume.assumeTrue(OSUtils.isWindows());
shellTask.handle(); shellTask.handle();
Assert.assertTrue(true); Assert.assertTrue(true);
} catch (Error | Exception e) { } catch (Error | Exception e) {

Loading…
Cancel
Save