All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openqa.selenium.os.WindowsProcessGroup Maven / Gradle / Ivy

Go to download

Selenium automates browsers. That's it! What you do with that power is entirely up to you.

There is a newer version: 4.24.0
Show newest version
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC 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.openqa.selenium.os;

import com.sun.jna.platform.win32.WinBase;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import org.openqa.selenium.Beta;
import org.openqa.selenium.WebDriverException;

import java.io.File;
import java.io.OutputStream;
import java.util.Map;

// This is a rewrite of
// http://svn.openqa.org/svn/selenium-rc/trunk/killableprocess/killableprocess.cpp
// in Java using JNA.

/**
 * Utility class for grouping a set of processes into a process group on
 * Windows. This is primarily used for processes that spawn child processes and
 * then disconnect from them: killing the parent process should kill the
 * children too. That sounds a bit more sinsiter than it actually is.
 */
@Beta
public class WindowsProcessGroup implements OsProcess {
  private Kernel32 Kernel32;
  private String cmd;
  private WinNT.HANDLE hJob;
  private String workingDirectory = null;

  public WindowsProcessGroup(String executable, String... args) {
    Kernel32 = Kernel32.INSTANCE;

    StringBuilder toExecute = new StringBuilder();
    toExecute.append(executable);

    for (String arg : args) {
      toExecute.append(" ");
      toExecute.append(quote(arg));
    }

    cmd = toExecute.toString();
  }

  private String quote(String toQuote) {
    if (toQuote.indexOf(' ') != -1) {
      return '"' + toQuote + '"';
    }
    return toQuote;
  }

  public Map getEnvironment() {
    throw new UnsupportedOperationException("getEnvironment");
  }

  public void setEnvironmentVariable(String name, String value) {
    throw new UnsupportedOperationException("setEnvironmentVariable");
  }

  public void copyOutputTo(OutputStream out) {
    throw new UnsupportedOperationException("copyOutputTo");
  }

  public void setInput(String allInput) {
    throw new UnsupportedOperationException("setInput");
  }

  public void setWorkingDirectory(File workingDirectory) {
    this.workingDirectory = workingDirectory.getAbsolutePath();
  }

  public void executeAsync() {
    WinBase.STARTUPINFO si = new WinBase.STARTUPINFO();
    si.clear();
    WinBase.PROCESS_INFORMATION.ByReference pi = new WinBase.PROCESS_INFORMATION.ByReference();
    pi.clear();
    Kernel32.JOBJECT_EXTENDED_LIMIT_INFORMATION jeli =
        new Kernel32.JOBJECT_EXTENDED_LIMIT_INFORMATION.ByReference();
    jeli.clear();
    Kernel32.JOBOBJECT_BASIC_UI_RESTRICTIONS uli =
        new Kernel32.JOBOBJECT_BASIC_UI_RESTRICTIONS.ByReference();
    uli.clear();

    // Call SetHandleInformation. Take a look in SocketLock.cs

    hJob = Kernel32.CreateJobObject(null, null);
    if (hJob.getPointer() == null) {
      throw new WebDriverException("Cannot create job object: " + Kernel32.GetLastError());
    }

    // Hopefully, Windows will kill the job automatically if this process dies
    // But beware!  Process Explorer can break this by keeping open a handle to all jobs!
    // http://forum.sysinternals.com/forum_posts.asp?TID=4094
    jeli.BasicLimitInformation.LimitFlags = Kernel32.JOB_OBJECT_LIMIT_BREAKAWAY_OK |
        Kernel32.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;

    if (!Kernel32.SetInformationJobObject(hJob, Kernel32.JobObjectExtendedLimitInformation, jeli.getPointer(), jeli.size())) {
      throw new WebDriverException("Unable to set extended limit information on the job object: " + Kernel32.GetLastError());
    }

    // crete job in sandbox with own global atom table
    uli.UIRestrictionsClass = Kernel32.JOB_OBJECT_UILIMIT_GLOBALATOMS;

    if (!Kernel32.SetInformationJobObject(hJob, Kernel32.JobObjectBasicUIRestrictions, uli.getPointer(), uli.size())) {
      throw new WebDriverException("Unable to set ui limit information on the job object: " + Kernel32.GetLastError());
    }

    WinDef.DWORD creationFlags = new WinDef.DWORD(
        Kernel32.CREATE_SUSPENDED |          // Suspend so we can add to job
        Kernel32.CREATE_BREAKAWAY_FROM_JOB   // Allow ourselves to breakaway from Vista's PCA if necessary
    );

    // Start the child process
    boolean result = Kernel32.CreateProcess(null, // No module name (use command line).
        cmd,   // Command line.
        null,  // Process handle not inheritable.
        null,  // Thread handle not inheritable.
        false, // Set handle inheritance to FALSE.
        creationFlags, // Set creation flags
        null,  // Use parent's environment block.
        workingDirectory,  // Use provided working directory, parent's directory if null.
        si,    // Pointer to STARTUPINFO structure.
        pi);   // Pointer to PROCESS_INFORMATION structure.
    if (!result) {
      throw new WebDriverException("Failed to create the process: " + Kernel32.GetLastError());
    }

    if (!Kernel32.AssignProcessToJobObject(hJob, pi.hProcess)) {
      throw new WebDriverException("Cannot assign process to job: " + Kernel32.GetLastError());
    }

    if (Kernel32.ResumeThread(pi.hThread) <= 0) {
      throw new WebDriverException("Cannot resume thread: " + Kernel32.GetLastError());
    }

    Kernel32.CloseHandle(pi.hThread);
    Kernel32.CloseHandle(pi.hProcess);
  }

  public void waitFor() throws InterruptedException {
    // no-op
  }

  public void waitFor(long timeout) throws InterruptedException {
	    // no-op
	  }

  public void checkForError() {
    // no-op
  }

  public int destroy() {
    if (!isRunning()) {
      return 0; // Hard code the return value
    }

    // This seems a trifle brutal. Oh well. Brutal it is.
    Kernel32.TerminateJobObject(hJob, 666);
    Kernel32.CloseHandle(hJob);
    hJob = null;

    return 0;
  }

  public int getExitCode() {
    return 0;
  }

  public boolean isRunning() {
    return hJob != null;
  }

  public String getStdOut() {
    throw new UnsupportedOperationException("getStdOut");
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy