Package uk.ac.uea.threadr

Source Code of uk.ac.uea.threadr.Threadr

package uk.ac.uea.threadr;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;

import uk.ac.uea.threadr.internal.MemoryMonitor;
import uk.ac.uea.threadr.internal.logging.EventLogger;
import uk.ac.uea.threadr.internal.testing.SafetyTester;
import uk.ac.uea.threadr.internal.vmhandler.VMWrapper;

/**
* <p>Threadr public API. Contains all the methods required to use the Threadr
* API to execute tasks in a parallel manner based on their thread safety
* status.</p>
* <p>Threadr requires a collection of objects that implement the
* {@link ParallelTask} interface. An optional set of objects that implement
* the {@link ThreadTest} interface if specific, non-standard tests are needed.
* </p>
* <p>Results from this API are returned wrapped in a {@link ThreadrResult}
* object, which is a mapping of unique tests to their results.</p>
*
* @author Jordan Woerner
* @version 1.5
*/
public final class Threadr {
 
  /**
   * Logger for the Threadr API, outputs to the 'log' folder of the project
   * using Threadr.
   * 
   * @since 1.0
   */
  public static final Logger logger = Logger.getLogger("uk.ac.uea.threadr");
  /**
   * Mapping of {@link ParallelTasks} and their {@link AbstractReturnType}
   * results.
   */
  private ThreadrResult tasks;
  /** List of unique {@link ThreadTest} for thread safety testing. */
  private Set<ThreadTest> tests;
  /** Results from the thread safety testing. */
  private Map<Class<?>, ThreadSafety> threadSafeties;
  /** An array of parameters to be passed to the new VM instances. */
  private String[] vmParameters;
  /** A {@link SequentialHandler} to handle unsafe tasks. */
  private SequentialHandler handler;
  /** Monitors the memory used by Threadr. */
  private MemoryMonitor monitor;
 
  /**
   * <p>Sets up Threadr with no tasks to work on and no external tests
   * loaded.</p>
   * <p>Threadr expects to be passed an array or List of classes that extend
   * the {@link ParallelTask} interface. These tasks will be assessed for
   * thread safety, and executed based on the result of this.</p>
   * <p>Should the tasks need specialised testing for thread safety, Threadr
   * can be passed an array or List of {@link ThreadTest} classes to use
   * during testing.</p>
   *
   * @since 1.0
   */
  public Threadr() {
   
    this.tasks = new ThreadrResult();
    this.tests = new HashSet<>();
    this.threadSafeties = new HashMap<>();
    this.handler = new IgnoreSequential();
    this.monitor = MemoryMonitor.getInstance();
  }
 
  /**
   * <p>Sets up Threadr with the specified array of tasks to work on.</p>
   * <p>Threadr expects to be passed an array or List of classes that extend
   * the {@link ParallelTask} interface. These tasks will be assessed for
   * thread safety, and executed based on the result of this.</p>
   * <p>Should the tasks need specialised testing for thread safety, Threadr
   * can be passed an array or List of {@link ThreadTest} classes to use
   * during testing.</p>
   *
   * @param tasks An array of objects that implement the ParallelTask
   * interface.
   * @since 1.0
   */
  public Threadr(ParallelTask[] tasks) {
   
    this.tasks = new ThreadrResult();
    this.tests = new HashSet<>();
    this.threadSafeties = new HashMap<>();
    this.handler = new IgnoreSequential();
    this.monitor = MemoryMonitor.getInstance();
   
    addTasks(tasks); // Add the tasks to the task pool.
  }
 
  /**
   * <p>Sets up Threadr with the specified List of tasks to work on. Tasks
   * must be derived from the ParallelTask interface to be supported.</p>
   * <p>Threadr expects to be passed an array or List of classes that extend
   * the {@link ParallelTask} interface. These tasks will be assessed for
   * thread safety, and executed based on the result of this.</p>
   * <p>Should the tasks need specialised testing for thread safety, Threadr
   * can be passed an array or List of {@link ThreadTest} classes to use
   * during testing.</p>
   *
   * @param tasks A List of objects that implement the ParallelTask
   * interface.
   * @since 1.0
   */
  public Threadr(List<? extends ParallelTask> tasks) {
   
    this.tasks = new ThreadrResult();
    this.tests = new HashSet<>();
    this.threadSafeties = new HashMap<>();
    this.handler = new IgnoreSequential();
    this.monitor = MemoryMonitor.getInstance();
   
    addTasks(tasks); // Add the tasks to the task pool.
  }
 
  /**
   * <p>Sets up Threadr with the specified array of tasks and external tests.
   *  Tasks must be derived from the ParallelTask interface to be supported,
   * external tests must extend the ThreadTest interface to be loaded.</p>
   * <p>Threadr expects to be passed an array or List of classes that extend
   * the {@link ParallelTask} interface. These tasks will be assessed for
   * thread safety, and executed based on the result of this.</p>
   * <p>Should the tasks need specialised testing for thread safety, Threadr
   * can be passed an array or List of {@link ThreadTest} classes to use
   * during testing.</p>
   *
   * @param tasks An array of objects that implement the ParallelTask
   * interface.
   * @param tests An array of objects that implement the ThreadTest 
   * interface.
   * @since 1.5
   */
  public Threadr(ParallelTask[] tasks, ThreadTest[] tests) {
   
    this.tasks = new ThreadrResult();
    this.tests = new HashSet<>();
    this.threadSafeties = new HashMap<>();
    this.handler = new IgnoreSequential();
    this.monitor = MemoryMonitor.getInstance();
   
    addTasks(tasks);
    addTests(tests);
  }
 
  /**
   * <p>Sets up Threadr with the specified list of tasks and external tests.
   * Tasks must be derived from the ParallelTask interface to be supported,
   * external tests must extend the ThreadTest interface to be loaded.</p>
   * <p>Threadr expects to be passed an array or List of classes that extend
   * the {@link ParallelTask} interface. These tasks will be assessed for
   * thread safety, and executed based on the result of this.</p>
   * <p>Should the tasks need specialised testing for thread safety, Threadr
   * can be passed an array or List of {@link ThreadTest} classes to use
   * during testing.</p>
   *
   * @param tasks A {@link List} of objects that implement the ParallelTask
   * interface.
   * @param tests A {@link List} of objects that implement the ThreadTest 
   * interface.
   * @since 1.0
   */
  public Threadr(List<? extends ParallelTask> tasks,
      List<? extends ThreadTest> tests) {
   
    this.tasks = new ThreadrResult();
    this.tests = new HashSet<>();
    this.threadSafeties = new HashMap<>();
    this.handler = new IgnoreSequential();
    this.monitor = MemoryMonitor.getInstance();
   
    addTasks(tasks);
    addTests(tests);
  }
 
  /**
   * Sets the the external logging facility of Threadr. Calling this will
   * cause Threadr to create a folder 'logs' in the working directory of the
   * application.
   *
   * @since 1.3
   */
  public final void enableLogging() {
   
    try {
      /* Set up external logging. */
      EventLogger.setup();
    } catch (IOException e) {
      /* Thrown when EventLogger cannot set up the FileHandlers. */
      System.err.println("Failed to set up logging!");
      System.err.println(e.getMessage());
    }
  }
 
  /**
   * Adds the specified task to the Map of classes to be tested and run.
   *
   * @param task The {@link ParallelTask} to add to this instance of Threadr.
   * @since 1.5
   */
  public final void addTask(ParallelTask task) {
   
    this.tasks.addTask(task);
  }
 
  /**
   * Adds the specified task to the Map of classes to be tested and run. The
   * thread safeness of a task may be specified using this method. If null is
   *  passed as the second parameter, the task will have its thread safety
   * evaluated by the framework at runtime.
   *
   * @param task The {@link ParallelTask} to add to this instance of Threadr.
   * @param safeness The thread safety of the task. Can be one of;
   * {@link ThreadSafety#THREAD}, {@link ThreadSafety#VM}, or
   * {@link ThreadSafety#SEQUENTIAL}.
   * @return Returns the previous {@link ThreadSafety} value for the provided
   * task if one exists, or null otherwise.
   * @since 1.5
   */
  public final ThreadSafety addTask(ParallelTask task, ThreadSafety safeness) {
   
    this.tasks.addTask(task);
    return threadSafeties.put(task.getClass(), safeness);
  }
 
  /**
   * Adds the specified tasks to the Map of classes to be tested and run.
   *
   * @param tasks A {@link List} of objects that implement the ParallelTask
   * interface.
   * @since 1.0
   */
  public final void addTasks(List<? extends ParallelTask> tasks) {
   
    for (Object task : tasks) {
      if (!this.tasks.addTask((ParallelTask)task)) {
        logger.warning("Duplicate task provided!");
      }
    }
  }
 
  /**
   * <p>Adds the specified array of tasks to the Map of classes to be tested
   * and run.</p>
  
   * @param tasks An array of objects that implement the ParallelTask
   * interface.
   * @since 1.0
   */
  public final void addTasks(ParallelTask[] tasks) {
   
    /* Use the List based addTasks method, less repeating code. */
    addTasks(Arrays.asList(tasks));
  }
 
  /**
   * Adds the tasks held in the provided {@link Map} to the Map of classes to
   *  be tested and run. Also stores the {@link ThreadSafety} associated with
   * the class inside the provided map.
   *
   * ThreadSafety can be one of; {@link ThreadSafety#THREAD},
   * {@link ThreadSafety#VM}, or {@link ThreadSafety#SEQUENTIAL}.
   *
   * @param tasks A mapping of the {@link ParallelTask} objects to add to
   * this instance of Threadr to their thread safeness as a
   * {@link ThreadSafety} enumeration. The mapped ThreadSafety can be null, which
   *  will force the framework to determine the thread safety at runtime.
   * @return Returns a mapping of the existing {@link ThreadSafety} values for
   * and classes that already existed in the test results.
   * @since 1.5
   */
  public final Map<? extends ParallelTask, ThreadSafety> addTasks(
      Map<? extends ParallelTask, ThreadSafety> tasks) {
   
    Map<ParallelTask, ThreadSafety> oldResults = new HashMap<>();
   
    for (ParallelTask task : tasks.keySet()) {
      this.tasks.addTask(task);
      /* Get the desired ThreadSafety and the old result. */
      ThreadSafety newResult = tasks.get(task);
      ThreadSafety oldResult = threadSafeties.put(task.getClass(), newResult);
      /* If the existing result was not null, we'll return that. */
      if (oldResult != null) {
        oldResults.put(task, oldResult);
      }
    }
   
    return oldResults;
  }
 
  /**
   * Adds a single test to be used during thread safety testing. Duplicate
   * tests will not be added to the Set.
   *
   * @param test The test to add as an implementation of the
   * {@link ThreadTest} interface.
   * @since 1.5
   */
  public final void addTest(ThreadTest test) {
   
    if (!this.tests.add((ThreadTest)test)) {
      logger.warning("Duplicate test provided!");
    }
  }
 
  /**
   * <p>Add a list of tests to the Set of tests to be used during thread
   * safety testing. Duplicate tests will not be added to the Set.</p>
   *
   * @param tests A list of objects that implement the ThreadTest interface.
   * @since 1.0
   */
  public final void addTests(List<? extends ThreadTest> tests) {
   
    for (Object test : tests) {
      if (!this.tests.add((ThreadTest)test)) {
        logger.warning("Duplicate test provided!");
      }
    }
  }
 
  /**
   * <p>Add an array of tests to the Set of tests to be used during thread
   * safety testing. Duplicate tests will not be added to the Set.</p>
   *
   * @param tests An array of objects that implement the ThreadTest
   * interface.
   * @since 1.0
   */
  public final void addTests(ThreadTest[] tests) {
   
    /* Use the existing addTests code to save time. */
    addTests(Arrays.asList(tests));
  }
 
  /**
   * <p>Allows custom parameters to be passed to new instances of the Java
   * Virtual Machine.</p>
   * <p>Invalid parameters may cause the VM to fail to launch,
   * this won't be detected until execution and will cause issues with any
   * tasks assigned to new Virtual Machines.</p>
   * <p>Available JVM options can be found
   * <a href="http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html">
   * here</a></p>
   *
   * @param args And array of Strings, with each element being a command line
   *  option or parameter value for an option.
   * @since 1.1
   */
  public final void setVMParameters(String...args) {
   
    this.vmParameters = args;
  }
 
  /**
   * Sets the {@link SequentialHandler} this instance of Threadr should use
   * to handle {@link ParallelTask}s that are not thread or virtual machine
   * safe.
   *
   * @param newHandler The new SequentialHandler to use.
   * @since 1.2
   */
  public final void setSequentialHandler(SequentialHandler newHandler) {
   
    logger.info(String.format("Using SequentialHandler '%s'.",
        newHandler.getClass().getName()));
    this.handler = newHandler;
  }
 
  /**
   * Gets the tasks to be executed by this {@link Threadr} instance.
   *
   * @return The tasks as a {@link Set} of {@link ParallelTask} instances.
   * @since 1.4
   */
  public final Set<? extends ParallelTask> getTasks() {
   
    return tasks.getTasks();
  }
 
  /**
   * Gets the thread safeness tests to be used this {@link Threadr} instance.
   *
   * @return The tasks as a {@link Set} of {@link ThreadTest} instances.
   * @since 1.4
   */
  public final Set<? extends ThreadTest> getTests() {
   
    return tests;
  }
 
  /**
   * Gets the stored thread safeness testing results from this instance of
   * {@link Threadr}. The classes for each task are mapped to their result as
   *  a {@link ThreadSafety} value.
   *
   * @return The mapping of {@link Class} objects to their
   * {@link ThreadSafety} values for thread safeness.
   * @since 1.5
   */
  public final Map<Class<?>, ThreadSafety> getThreadSafetyResults() {
   
    return threadSafeties;
  }
 
  /**
   * Gets the command line parameters to be used when creating new Java
   * Virtual Machine instances.
   *
   * @return The parameters as an array of Strings.
   * @since 1.4
   */
  public final String[] getVMParameters() {
   
    return vmParameters;
  }
 
  /**
   * Tests the thread safety of the tasks currently held in this instance of
   * {@link Threadr}. This method is called during the
   * {@link Threadr#execute()} method to ensure that the thread safety values
   *  are up to date and exist for all tasks. To see the thread safety test
   * results, call the {@link Threadr#getThreadSafetyResults()} method.
   * 
   *  @since 1.5
   */
  public final void testSafety() {
   
    SafetyTester tester = new SafetyTester();
    tester.provideTests(tests); // Add our custom tests.
   
    /* Test all the unique classes passed to the framework. */
    for (Object t : tasks.getTasks()) {
      if (!threadSafeties.containsKey(t.getClass())
          || threadSafeties.get(t.getClass()) == null) {
        /* Test the thread safeness of tasks that haven't been tested
           or tasks that have a null result. */
        ThreadSafety threadSafety = tester.testSafety(t);     
        threadSafeties.put(t.getClass(), threadSafety);
      }
    }
  }
 
  /**
   * <p>Tests all the tasks provided to the API using the built in tests and
   * any provided tests.</p>
   * <p>Classes are tested for their thread safety, and based on the results
   * of the testing, are run in their own thread, in a new instance of the
   * Java Virtual Machine, or sequentially.</p>
   *
   * @return A mapping of the tasks to their AbstractReturnType results as a
   * {@link ThreadrResult} instance.
   * @since 1.0
   */
  public final ThreadrResult execute() {
   
    if (tasks.size() == 0) {
      /* Don't bother running anything if there are no tasks. */
      logger.info("No tasks provided.");
      return null; // If things go wrong, just return null.
    }
   
    /* Start the memory monitoring thread. */
    Thread monitorThread = new Thread(monitor);
    monitorThread.start();
   
    /* Set up the ExecutorService that will handle threading. */
    ExecutorService executor = Executors.newCachedThreadPool();
   
    testSafety(); // Run the thread safety tests.
   
    Map<ParallelTask, Future<AbstractReturnType<?>>> futures =
        new HashMap<>();
   
    /* Execute each task based on whether they are thread or VM safe. */
    for (Object t : tasks.getTasks()) {     
      ParallelTask task = (ParallelTask)t;
     
      switch (threadSafeties.get(t.getClass())) {
      case SEQUENTIAL: // If the task is not safe.
        AbstractReturnType<?> result = handler.handle(task);
        if (result != null) {
          /* If the handler returned a result, store it. */
          tasks.addResult(task, result);
        }
        break;
      case THREAD: // The task is thread safe.
        futures.put(task, runThread(executor, task));
        break;
      case VM: // The task is virtual-machine safe.
        futures.put(task, runVM(executor, task));
        break;
      default:
        logger.warning("Unknown test result found during execution.");
      }
    }
   
    /* Attempt to grab the returned AbstractReturnTypes. */
    for (Object o : futures.keySet()) {
      ParallelTask task = (ParallelTask)o;
      Future<AbstractReturnType<?>> result = futures.get(task);
     
      try {
        if (result != null) {
          tasks.addResult(task, result.get());
        } else {
          /* Remove tasks that fail to return. */
          logger.warning(String.format(
              "Task %s failed to return a result.",
              task.getClass().getName()));
          tasks.removeResult(task);
        }
      } catch (ExecutionException exEx) {
        /* If something goes wrong during execution, log the error. */
        logger.severe(String.format("Execution error in class %s."
            + "\nMessage: %s",
            task.getClass().getName(), exEx.getMessage()));
      } catch (InterruptedException inEx) {
        logger.severe(String.format("Execution of class %s "
            + "interrupted.\nReason: %s",
            task.getClass().getName(), inEx.getMessage()));
      }
    }
   
    executor.shutdown(); // Done with the ExecutorService now.
   
    /* Perform any post-execution operations in the selected handler. */
    ThreadrResult sequentialResults = handler.postExecution();
    if (sequentialResults != null) {
      tasks.combine(sequentialResults);
    }
    /* Stop the memory monitor. */
    try {
      monitorThread.join();
    } catch (InterruptedException iEx) {
      logger.warning("Could not join memory monitor in main thread.");
    }
   
    return tasks;
  }
 
  /**
   * <p>Runs the provided ParallelTask in a new thread. Results of execution
   * are passed back contained in an AbstractReturnType.</p>
   *
   * @param executor The ExecutorService to run the provided ParallelTask in
   * a new thread.
   * @param task The ParallelTask to be executed in a new thread. Task is
   * final to prevent modification of the reference by the thread.
   * @return The results of execution as an AbstractReturnType contained in a
   *  Future instance.
   * @since 1.0
   */
  private final Future<AbstractReturnType<?>> runThread(
      final ExecutorService executor,
      final ParallelTask task) {
   
    /* Create a new Future holding an AbstractReturnType. */
    Future<AbstractReturnType<?>> result = executor.submit(task);
   
    return result; // Return the Future.
  }
 
  /**
   * <p>Runs the provided ParallelTask in a new Java Virtual machine
   * instance. Results of execution are passed back contained in an
   * AbstractReturnType.</p>
   *
   * @param executor The ExecutorService to handle the thread that will track
   *  external JVM progress.
   * @param task The ParallelTask to execute in a new JVM instance.
   * @return The results of execution as an AbstractReturnType contained in a
   *  Future instance.
   * @since 1.0
   */
  private final Future<AbstractReturnType<?>> runVM(
      final ExecutorService executor,
      final ParallelTask task) {
   
    Future<AbstractReturnType<?>> result = null;
    /* Stick the task inside a VMWrapper and send it to be executed. */
    VMWrapper newVM = null;
    if (vmParameters != null
        && vmParameters.length > 0) {
      /* If there are custom parameters, pass them to the VM instance. */
      newVM = new VMWrapper(task, vmParameters);
    } else {
      /* Otherwise, just create the instance. */
      newVM = new VMWrapper(task);
    }
   
    /* Try and execute the provided task in a new VM. */
    result = executor.submit(newVM);
   
    return result;
  }
 
  /**
   * Gets the {@link MemoryMonitor} attached to this instance of Threadr.
   *
   * @return The MemoryMonitor in use.
   */
  public final MemoryMonitor getMemoryMonitor() {
   
    return monitor;
  }
 
  /**
   * <p>Resets Threadr to its starting state. All tasks, results, tests and
   * virtual machine parameters are removed.</p>
   *
   * @since 1.1
   */
  public final void reset() {
   
    tasks.clear();
    tests.clear();
    vmParameters = null;
    monitor.clear();
  }
 
  /**
   * <p>Set the detail of the logging output of Threadr.</p>
   *
   * @param output The output detail as a logging API Level.
   * @since 1.0
   */
  public final void setOutputLevel(Level output) {
   
    EventLogger.setLevel(output);
  }
 
  /**
   * Gets details about this instance of Threadr. Details include the number
   * of tasks, the number of tests and the Virtual Machine parameters in use.
   *
   * @return The details of this instance as a String.
   * @since 1.3
   */
  @Override
  public String toString() {
   
    StringBuilder sb = new StringBuilder();
    sb.append("Threadr - Tasks: ").append(tasks.size());
    sb.append(", Tests: ").append(tests.size());
    sb.append(", VM Parameters:\n\t");
    for (String param : vmParameters) {
      sb.append(param).append(", ");
    }
   
    return sb.toString();
  }
}
TOP

Related Classes of uk.ac.uea.threadr.Threadr

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.