Package wowodc.background.tasks

Source Code of wowodc.background.tasks.T05MultiThreadedEOFTask$ChildPrimeTask

package wowodc.background.tasks;

import java.text.DecimalFormat;
import java.text.Format;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;

import org.apache.log4j.Logger;

import wowodc.background.utilities.Utilities;
import wowodc.eof.ResultItem;
import wowodc.eof.TaskInfo;

import com.webobjects.eocontrol.EOEditingContext;
import com.webobjects.eocontrol.EOGlobalID;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSTimestamp;

import er.extensions.concurrency.ERXExecutorService;
import er.extensions.concurrency.ERXTask;
import er.extensions.concurrency.IERXPercentComplete;
import er.extensions.concurrency.IERXStoppable;
import er.extensions.eof.ERXEOControlUtilities;
import er.extensions.foundation.IERXStatus;

/**
* A task that <em>returns</em> an EOGlobalID result.
*
* What does this demonstration task do?
*
* A "TaskInfo" entity is created for every run of this task.
* Its attributes include
* <ul>
* <li>startNumber
* <li>endNumber
* <li>startTime
* <li>endTime
* </ul>
*
* For a random amount of time between 5 and 15 seconds, and
* starting at a random number, this task begins looking for prime numbers
* for every
*
* @author kieran
*/
public class T05MultiThreadedEOFTask extends ERXTask<EOGlobalID> implements Callable<EOGlobalID>, IERXStatus , IERXPercentComplete, IERXStoppable {
 
  private static final Logger log = Logger.getLogger(T05MultiThreadedEOFTask.class);
 
  // Duration of the example task in milliseconds
  // Random between 5 and 15 seconds
  private final long _taskDuration;
 
  // Task elapsed time in milliseconds
  private long _elapsedTime = 0l;
 
  // Value between 0.0 and 1.0 indicating the task's percentage complete
  private double _percentComplete = 0.0d;
 
  // A message indicating current status
  private String _status = "Starting...";
 
  private final long _startNumber = 0;
  private long _endNumber;
 
  // Base quantity - we use this plus a random amount up to the same quantity
  // just to ensure slightly varied processing times per task thread.
  // Otherwise all threads are starting and stopping at the same time
  // and it looks too fake :-)
  private final int _childBatchBaseQuantity = 2000;
 
  // Volatile since it is being updated from child threads
  private volatile long _count = 0;
 
  // We assign an ID to each child just for demo purposes so that users can
  // see that different child tasks are being started and finished in the App Monitor page.
  // This value is passed to child task in constructor and used in toString.
  private int _nextChildIDValue = 1;
 
  private volatile boolean _isStopped = false;
 
  // Just so we can have this for toString for the AppMonitor display
  private int _parentTaskPrimaryKey = -1;
 
  // Lazy initialization of static variable.
  // This prevents more than N threads in this pool even if multiple instances of
  // this task is run, which may or may not be what you want.
  // If you want a pool per task, then use a lazy initialized instance variable
  private static class ChildTaskPool {
    final static ExecutorService EXECUTOR_SERVICE = ERXExecutorService.newFiniteThreadPool(4);
  }
 
  public T05MultiThreadedEOFTask() {
    _taskDuration = 15000;
  }
 
  /**
   * Use a demo duration parameter rather than default random demo duration.
   *
   * @param demoTaskDuration duration in milliseconds
   */
  public T05MultiThreadedEOFTask(long demoTaskDuration) {
    _taskDuration = demoTaskDuration;
  }
 
  private EOGlobalID _resultGid;

  @Override
  public EOGlobalID _call() throws Exception {
    // Start at zero to gauge performance rate with different numbers of threads and OSCs
    //_startNumber = Utilities.newStartNumber();
    _elapsedTime = 0;
    Format wholeNumberFormatter = new DecimalFormat("#,##0");
   
    long startTime = System.currentTimeMillis();
   
    EOEditingContext ec = newEditingContext();
    ec.lock();
    try {
      // Array for monitoring completed tasks to ensure normal completion
      NSMutableArray<Future<?>> childFutures = new NSMutableArray<Future<?>>();
      // Create the new TaskInfo
      TaskInfo taskInfo = ERXEOControlUtilities.createAndInsertObject(ec, TaskInfo.class);
     
      // Task start time
      taskInfo.setStartTime(new NSTimestamp(startTime));
     
      taskInfo.setStartNumber(_startNumber);
      taskInfo.setDuration(_taskDuration);
     
      ec.saveChanges();
      _resultGid = ec.globalIDForObject(taskInfo);
      _parentTaskPrimaryKey = (Integer) taskInfo.rawPrimaryKey();
     
      // Initialize loop variables
      long childTaskStartNumber = _startNumber;
      int incrementQuantity = _childBatchBaseQuantity + Utilities.sharedRandom().nextInt(_childBatchBaseQuantity);
      long childTaskEndNumber =  childTaskStartNumber + incrementQuantity;
     
     
      // Loop for a period of time
      while (_elapsedTime < _taskDuration && !_isStopped) {
        ChildPrimeTask childTask = new ChildPrimeTask(_nextChildIDValue, _resultGid, childTaskStartNumber, childTaskEndNumber);
        _nextChildIDValue++;
       
        boolean isRejected = true;
        while (isRejected && !ChildTaskPool.EXECUTOR_SERVICE.isShutdown() && !_isStopped) {
          try {
            Future<?> future = ChildTaskPool.EXECUTOR_SERVICE.submit(childTask);
           
            if (log.isInfoEnabled())
              log.info("Submitted task corresponding to " + future);
            isRejected = false;
            childFutures.add(future);
           
            // For the sake of demo, we assume all child tasks complete their work.
            _endNumber = childTaskEndNumber;
          } catch (RejectedExecutionException e) {
            try {
              Thread.sleep(2000);
              removeCompletedFutures(childFutures);
            } catch (InterruptedException e1) {
              stop();
            }
          }
        }
       
        childTaskStartNumber = childTaskEndNumber + 1;
        incrementQuantity = _childBatchBaseQuantity + Utilities.sharedRandom().nextInt(_childBatchBaseQuantity * 2);
        childTaskEndNumber =  childTaskStartNumber + incrementQuantity;

        _elapsedTime = System.currentTimeMillis() - startTime;
       
        // Update progress variables
        _percentComplete = (double)(_elapsedTime) / (double)_taskDuration;
        _status = wholeNumberFormatter.format(_count) + " numbers checked for prime qualification";

      }
     
      if (_isStopped) {
        _status = "Stopped";
      }
     
      // Wait for all child tasks to finish
      while (childFutures.count() > 0) {
        removeCompletedFutures(childFutures);
        Thread.sleep(1000);
      }
     
      // Complete the stats
      // Refresh it since the object has been already updated (its relationship) and saved on ChildThreads
      ERXEOControlUtilities.refreshObject(taskInfo);
      taskInfo.setEndNumber(_endNumber);
      taskInfo.setEndTime(new NSTimestamp());
      taskInfo.setWorkflowState(TaskInfo.WORKFLOW_PRIME_CHECKED);
      ec.saveChanges();
     
      _resultGid = ec.globalIDForObject(taskInfo);
     
    } finally {
      ec.unlock();
    }
   
    return _resultGid;
  }
 
  /**
   * Removes completed futures from the futures array.
   *
   * @param futures array of futures
   */
  public void removeCompletedFutures(NSMutableArray<Future<?>> futures) {
    Iterator<Future<?>> iterator = futures.iterator();
    while (iterator.hasNext()) {
      Future<?> future = iterator.next();
     
      if (future.isDone()) {
        // Before removal, we can take this opportunity to check for errors in the child tasks
        try {
          Object result = future.get();
        } catch (Exception e) {
          // An exception here means the task did not complete normally
          throw new RuntimeException("Unexpected exception occurred in ChildTask", e);
        }

        iterator.remove();
      }
    }
  }
 
  /* (non-Javadoc)
   * @see er.extensions.concurrency.IERXPercentComplete#percentComplete()
   */
  public Double percentComplete() {
    return _percentComplete;
  }

  /* (non-Javadoc)
   * @see er.extensions.foundation.IERXStatus#status()
   */
  public String status() {
    return _status;
  }

  /* (non-Javadoc)
   * @see er.extensions.concurrency.IERXStoppable#stop()
   */
  public void stop() {
    log.info("The task was stopped by the user.");
    _isStopped = true;
    _status = "Stopping";
  }
 
  @Override
  public String toString() {
    StringBuilder b = new StringBuilder();
    b.append(this.getClass().getSimpleName());
    if (_parentTaskPrimaryKey > 0) {
      b.append(": #").append(_parentTaskPrimaryKey);
    }
    return b.toString();
  }
 
  /**
   * A child task that will be used to process a batch of numbers in its own thread.
   *
   * Note we declare this as a non-static inner class so that the child thread can update the parent thread _count (for demo of volatile)
   *
   * @author kieran
   */
  private class ChildPrimeTask extends ERXTask implements Runnable, IERXStatus, IERXPercentComplete {
   
    private final int _childID;
    private EOGlobalID _childTaskInfoGID = null;
    private final long _childFromNumber;
    private final long _childToNumber;
   
    private long _childCurrentNumber;
   
   
    public ChildPrimeTask(int childID, EOGlobalID taskInfoGID, long fromNumber, long toNumber) {
      _childID = childID;
      _childTaskInfoGID = taskInfoGID;
      _childFromNumber = fromNumber;
      _childToNumber = toNumber;
     
      // Starting value
      _childCurrentNumber = fromNumber;
    }

    public Double percentComplete() {
      return Double.valueOf((_childCurrentNumber - _childFromNumber + 1) / (_childToNumber - _childFromNumber + 1));
    }

    public String status() {
      return "Checking " + _childCurrentNumber + " in range " + _childFromNumber + " - " + _childToNumber;
    }

    @Override
    public void _run() {
      EOEditingContext ec = newEditingContext();
      ec.lock();
      try {
        log.info("Started child in " + Thread.currentThread().getName() + " with OSC " + ec.parentObjectStore());
       
        TaskInfo taskInfo = (TaskInfo) ec.faultForGlobalID(_childTaskInfoGID, ec);
       
        while (_childCurrentNumber <= _childToNumber) {
          ResultItem resultItem = ERXEOControlUtilities.createAndInsertObject(ec, ResultItem.class);
          resultItem.setTaskInfo(taskInfo);
         
          resultItem.setNumberToCheck(_childCurrentNumber);

          if (Utilities.isPrime(_childCurrentNumber)) {
            log.info("==>> " + _childCurrentNumber + " is a PRIME number.");
            resultItem.setIsPrime(Boolean.TRUE);
          } else {
            log.debug(_childCurrentNumber + " is not a prime number but is a COMPOSITE number.");
            resultItem.setIsPrime(Boolean.FALSE);
          }
         
          // We could save changes once per child task, but let's do this to keep EOF busy for the demo.
          ec.saveChanges();
         
          // Update our number to check
          _childCurrentNumber++;
         
          // Update parent task count statistic
          _count++;
        }
      } finally {
        ec.unlock();
      }
    }
   
    //
    private String _toString = null;
   
    @Override
    public String toString() {
      if (_toString == null) {
        // We cache it since it will not change.
        StringBuilder b = new StringBuilder();
        b.append("ChildTask: #");
        b.append(_childID);
        b.append(", Parent ID=" + _parentTaskPrimaryKey);
        b.append(", From=" + _childFromNumber);
        b.append(", To=" + _childToNumber);
       
        _toString = b.toString();
      }
      return _toString;
    }
  }
}
TOP

Related Classes of wowodc.background.tasks.T05MultiThreadedEOFTask$ChildPrimeTask

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.