Bladeren bron

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
tison 5 jaren geleden
bovenliggende
commit
9224b49b58

+ 11 - 5
dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/OSUtils.java

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

+ 45 - 12
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.platform.win32.*;
 import com.sun.jna.ptr.IntByReference;
+import java.lang.reflect.Field;
+import org.apache.dolphinscheduler.common.utils.OSUtils;
 import sun.security.action.GetPropertyAction;
 
 import java.io.*;
@@ -31,10 +33,25 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import static com.sun.jna.platform.win32.WinBase.STILL_ACTIVE;
+import static java.util.Objects.requireNonNull;
 
 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;
 
@@ -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 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
      * is opened for atomic append directly and a FileOutputStream constructed
@@ -63,7 +96,7 @@ public class ProcessImplForWin32 extends Process {
                 sm.checkWrite(path);
             long handle = openForAtomicAppend(path);
             final FileDescriptor fd = new FileDescriptor();
-            fdAccess.setHandle(fd, handle);
+            setHandle(fd, handle);
             return AccessController.doPrivileged(
                     new PrivilegedAction<FileOutputStream>() {
                         public FileOutputStream run() {
@@ -102,30 +135,30 @@ public class ProcessImplForWin32 extends Process {
                 if (redirects[0] == ProcessBuilderForWin32.Redirect.PIPE)
                     stdHandles[0] = -1L;
                 else if (redirects[0] == ProcessBuilderForWin32.Redirect.INHERIT)
-                    stdHandles[0] = fdAccess.getHandle(FileDescriptor.in);
+                    stdHandles[0] = getHandle(FileDescriptor.in);
                 else {
                     f0 = new FileInputStream(redirects[0].file());
-                    stdHandles[0] = fdAccess.getHandle(f0.getFD());
+                    stdHandles[0] = getHandle(f0.getFD());
                 }
 
                 if (redirects[1] == ProcessBuilderForWin32.Redirect.PIPE)
                     stdHandles[1] = -1L;
                 else if (redirects[1] == ProcessBuilderForWin32.Redirect.INHERIT)
-                    stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
+                    stdHandles[1] = getHandle(FileDescriptor.out);
                 else {
                     f1 = newFileOutputStream(redirects[1].file(),
                             redirects[1].append());
-                    stdHandles[1] = fdAccess.getHandle(f1.getFD());
+                    stdHandles[1] = getHandle(f1.getFD());
                 }
 
                 if (redirects[2] == ProcessBuilderForWin32.Redirect.PIPE)
                     stdHandles[2] = -1L;
                 else if (redirects[2] == ProcessBuilderForWin32.Redirect.INHERIT)
-                    stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
+                    stdHandles[2] = getHandle(FileDescriptor.err);
                 else {
                     f2 = newFileOutputStream(redirects[2].file(),
                             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;
                         else {
                             FileDescriptor stdin_fd = new FileDescriptor();
-                            fdAccess.setHandle(stdin_fd, stdHandles[0]);
+                            setHandle(stdin_fd, stdHandles[0]);
                             stdin_stream = new BufferedOutputStream(
                                     new FileOutputStream(stdin_fd));
                         }
@@ -451,7 +484,7 @@ public class ProcessImplForWin32 extends Process {
                             stdout_stream = ProcessBuilderForWin32.NullInputStream.INSTANCE;
                         else {
                             FileDescriptor stdout_fd = new FileDescriptor();
-                            fdAccess.setHandle(stdout_fd, stdHandles[1]);
+                            setHandle(stdout_fd, stdHandles[1]);
                             stdout_stream = new BufferedInputStream(
                                     new FileInputStream(stdout_fd));
                         }
@@ -460,7 +493,7 @@ public class ProcessImplForWin32 extends Process {
                             stderr_stream = ProcessBuilderForWin32.NullInputStream.INSTANCE;
                         else {
                             FileDescriptor stderr_fd = new FileDescriptor();
-                            fdAccess.setHandle(stderr_fd, stdHandles[2]);
+                            setHandle(stderr_fd, stdHandles[2]);
                             stderr_stream = new FileInputStream(stderr_fd);
                         }
 

+ 2 - 1
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.junit.After;
 import org.junit.Assert;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -172,7 +173,7 @@ public class ShellTaskTest {
     @Test
     public void testHandleForWindows() throws Exception {
         try {
-            PowerMockito.when(OSUtils.isWindows()).thenReturn(true);
+            Assume.assumeTrue(OSUtils.isWindows());
             shellTask.handle();
             Assert.assertTrue(true);
         } catch (Error | Exception e) {