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

com.google.gwt.junit.remote.BrowserManagerProcess Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * Copyright 2008 Google Inc.
 * 
 * Licensed 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 com.google.gwt.junit.remote;

import java.text.NumberFormat;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Manages one web browser child process. This class contains a TimerTask which
 * tries to kill the managed process. A thread is created for each task to wait
 * for the process to exit and give a callback.
 * 
 * Invariants:
 * 
    *
  • Most of this code executes in a separate thread per process. Thus, the * API entry points lock this
  • *
  • The lock on this is removed before calling the childExited * callback. This prevents potential deadlock. *
*/ class BrowserManagerProcess { /** * Used to notify the caller of the constructor when a process exits. Note * that the childExited() method is called from a different thread than the * one that created the process. */ public interface ProcessExitCb { void childExited(int key, int exitValue); } /** * Kills the child process when fired, unless it is no longer the active * {@link BrowserManagerProcess#killTask}. */ private final class KillTask extends TimerTask { @Override public void run() { synchronized (BrowserManagerProcess.this) { /* * Verify we're still the active KillTask! If we're not the active * killTask, it means we've been rescheduled and a newer kill timer is * active. */ if (killTask == this && !deadOrDying) { logger.info("Timeout expired for: " + token); process.destroy(); deadOrDying = true; } } } } private static final Logger logger = Logger.getLogger(BrowserManagerProcess.class.getName()); /** * Compute elapsed time. * * @param startTime the time the process started * @return returns a string representing the number of seconds elapsed since * the process started. */ private static String getElapsed(long intervalMs) { NumberFormat nf = NumberFormat.getNumberInstance(); nf.setMaximumFractionDigits(3); return nf.format(intervalMs / 1000.0); } /** * Set to 'true' when the process exits or starts being killed. */ private boolean deadOrDying = false; /** * If non-null, the active TimerTask which will kill process * when it fires. */ private KillTask killTask; /** * The managed child process. */ private final Process process; /** * Timer instance passed in from BrowserManagerServer. */ private final Timer timer; /** * The key associated with process. */ private final int token; /** * Constructs a new ProcessManager for the specified process. * * @param timer timer passed in from BrowserManagerServer instance. * @param token the key to be used to identify this process. * @param process the process being managed * @param initKeepAliveMs the initial time to wait before killing * process */ public BrowserManagerProcess(final ProcessExitCb cb, Timer timer, final int token, final Process process, long initKeepAliveMs) { this.process = process; this.timer = timer; this.token = token; final long startTime = System.currentTimeMillis(); Thread cleanupThread = new Thread() { @Override public void run() { while (true) { try { int exitValue = process.waitFor(); doCleanup(cb, exitValue, token, System.currentTimeMillis() - startTime); return; } catch (InterruptedException e) { logger.log(Level.WARNING, "Interrupted waiting for process exit of: " + token, e); } } } }; cleanupThread.setDaemon(true); cleanupThread.setName("Browser-" + token + "-Wait"); cleanupThread.start(); keepAlive(initKeepAliveMs); } /** * Keeps the underlying process alive for keepAliveMs starting * now. If the managed process is already dead, cleanup is performed and the * method return false. * * @param keepAliveMs the time to wait before killing the underlying process * @return true if the process was successfully kept alive, * false if the process is already dead. */ public synchronized boolean keepAlive(long keepAliveMs) { assert (keepAliveMs > 0); if (!deadOrDying) { killTask = new KillTask(); timer.schedule(killTask, keepAliveMs); return true; } return false; } /** * Kills the underlying browser process. */ public synchronized void killBrowser() { if (!deadOrDying) { process.destroy(); deadOrDying = true; } } /** * Cleans up when the underlying process terminates. The lock must not be held * when calling this method or deadlock could result. * * @param cb the callback to fire * @param exitValue the exit value of the process * @param token the id of this browser instance * @param startTime the time the process started */ private void doCleanup(ProcessExitCb cb, int exitValue, int token, long intervalMs) { synchronized (this) { deadOrDying = true; } if (exitValue != 0) { logger.warning("Browser: " + token + " exited with bad status: " + exitValue); } else { logger.info("Browser: " + token + " process exited normally after " + getElapsed(intervalMs) + "s"); } cb.childExited(token, exitValue); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy