[skycastle-commits] SF.net SVN: skycastle: [216] trunk/skycastle/modules/utils/src/main/java/ org/skycastle/util

  • From: zzorn@xxxxxxxxxxxxxxxxxxxxx
  • To: skycastle-commits@xxxxxxxxxxxxx
  • Date: Thu, 04 Oct 2007 16:26:46 -0700

Revision: 216
          http://skycastle.svn.sourceforge.net/skycastle/?rev=216&view=rev
Author:   zzorn
Date:     2007-10-04 16:26:45 -0700 (Thu, 04 Oct 2007)

Log Message:
-----------
An initial JobQueue implementation.  It keeps a worker thread, and allows 
enqueying jobs into it.  Calculated progress for jobs based on previous 
execution times of that type of job, as well as by the current progress and 
duration.

Also did some updates in the MathUtilities and ParameterChecker to support the 
code.

Modified Paths:
--------------
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/MathUtils.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java

Added Paths:
-----------
    trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/AbstractJob.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/CancelReason.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/Job.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListener.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListenerAdapter.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobProgressListener.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueue.java
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueueImpl.java

Modified: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/MathUtils.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/MathUtils.java   
    2007-09-30 18:25:33 UTC (rev 215)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/MathUtils.java   
    2007-10-04 23:26:45 UTC (rev 216)
@@ -139,6 +139,30 @@
 
 
     /**
+     * Calculates a linearily interpolated value, given a start value and 
position, an end value and position,
+     * and the position to get the value at.
+     * <p/>
+     * First calculates the relative position, then does a normal linear 
interpolation between the start and end value,
+     * using the relative position as the interpolation factor.
+     *
+     * @param position
+     * @param startPosition
+     * @param endPosition
+     * @param startValue
+     * @param endValue
+     */
+    public static double interpolateDouble( final double position,
+                                            final double startPosition,
+                                            final double endPosition,
+                                            final double startValue,
+                                            final double endValue )
+    {
+        final double relativePosition = ( position - startPosition ) / ( 
endPosition - startPosition );
+        return startValue + relativePosition * ( endValue - startValue );
+    }
+
+
+    /**
      * Calculates a linearily interpolated value, given a start position and 
value, an end position and value,
      * and the position to get the value at.
      * <p/>
@@ -175,6 +199,42 @@
 
 
     /**
+     * Calculates a linearily interpolated value, given a start position and 
value, an end position and value,
+     * and the position to get the value at.
+     * <p/>
+     * If the position is outside start or end position, it is treated as if 
it was at the start or end value respectively.
+     * <p/>
+     * First calculates the relative position, then does a normal linear 
interpolation between the start and end value,
+     * using the relative position as the interpolation factor.
+     *
+     * @param position
+     * @param startPosition
+     * @param endPosition
+     * @param startValue
+     * @param endValue
+     */
+    public static double interpolateClampDouble( final double position,
+                                                 final double startPosition,
+                                                 final double endPosition,
+                                                 final double startValue,
+                                                 final double endValue )
+    {
+        // Clamp
+        double p = position;
+        if ( p < startPosition )
+        {
+            p = startPosition;
+        }
+        else if ( p > endPosition )
+        {
+            p = endPosition;
+        }
+
+        return interpolateDouble( p, startPosition, endPosition, startValue, 
endValue );
+    }
+
+
+    /**
      * Calculates a smoothly (cosine) interpolated value, given a start value, 
an end value,
      * and the position to get the value at.
      *
@@ -506,6 +566,22 @@
         return rolledValue;
     }
 
+    /**
+     * @return True if (x,y) is inside the axis aligned rectangle defined by 
the points (x1,y1) and (x2,y2), false otherwise.
+     */
+    public static boolean isInsideRectangle( final double x,
+                                             final double y,
+                                             final double x1,
+                                             final double y1,
+                                             final double x2,
+                                             final double y2 )
+    {
+        return x >= x1 &&
+               x < x2 &&
+               y >= y1 &&
+               y < y2;
+    }
+
     //======================================================================
     // Protected Methods
 

Modified: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java
        2007-09-30 18:25:33 UTC (rev 215)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java
        2007-10-04 23:26:45 UTC (rev 216)
@@ -179,6 +179,22 @@
     }
 
 
+    public static void checkNormalNumberInRangeInclusive( final double 
parameter,
+                                                 String parameterName,
+                                                 double minimumValueInclusize,
+                                                 double maximumValueInclusive )
+    {
+        checkNormalNumber( parameter, parameterName );
+
+        if ( parameter < minimumValueInclusize || parameter > 
maximumValueInclusive )
+        {
+            throwIllegalArgumentException( parameterName,
+                                           parameter,
+                                           "be in the range " + 
minimumValueInclusize + " (inclusive) to " + maximumValueInclusive + " 
(inclusive)" );
+        }
+    }
+
+
     public static void checkIntegerInRange( final int parameter,
                                             String parameterName,
                                             int minimumValueInclusize,
@@ -308,6 +324,20 @@
         }
     }
 
+    public static void checkLargerThan( final double parameter,
+                                        String parameterName,
+                                        double minimumValueExclusive,
+                                        String thresholdName )
+    {
+        if ( parameter <= minimumValueExclusive )
+        {
+            throwIllegalArgumentException( parameterName,
+                                           parameter,
+                                           "be larger than " + thresholdName + 
" (" + minimumValueExclusive + ")" );
+        }
+    }
+
+
     //======================================================================
     // Private Methods
 

Added: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/AbstractJob.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/AbstractJob.java
                            (rev 0)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/AbstractJob.java
    2007-10-04 23:26:45 UTC (rev 216)
@@ -0,0 +1,209 @@
+package org.skycastle.util.jobqueue;
+
+import org.skycastle.util.MathUtils;
+import org.skycastle.util.ParameterChecker;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@inheritDoc}
+ * <p/>
+ * This abstract class provides common functionality for Job:s, including 
prediction of the progress of the job based
+ * on earlier jobs of the same type, and the current duration and effort done 
by the job.
+ */
+public abstract class AbstractJob
+        implements Job
+{
+
+    //======================================================================
+    // Private Fields
+
+    private JobQueue myJobQueue = null;
+    private JobProgressListener myListener = null;
+    private boolean myCanceled = false;
+    private long myStartTime;
+
+    //======================================================================
+    // Private Constants
+
+    private static final Map<Class<? extends Job>, Double> thePredictedTimes_s 
= new HashMap<Class<? extends Job>, Double>();
+    private static final double PREDICTED_TIME_STABILITY_FACTOR = 0.8;
+    private static final double NANOSECONDS_TO_SECONDS = 0.000000001;
+    private static final double EFFORT_DONE_ESTIMATION_WEIGHT = 0.5;
+
+    //======================================================================
+    // Public Methods
+
+    //----------------------------------------------------------------------
+    // Job Implementation
+
+    public final void start( final JobQueue jobQueue, final 
JobProgressListener listener ) throws Throwable
+    {
+        ParameterChecker.checkNotNull( jobQueue, "jobQueue" );
+        myJobQueue = jobQueue;
+        myListener = listener;
+
+        myStartTime = System.nanoTime();
+
+        // Notify listeners about the initial estimated time, based on 
previous tasks of this type.
+        updateProgress( 0, "Starting" );
+
+        // Run the job
+        try
+        {
+            doJob();
+        }
+        catch ( Throwable throwable )
+        {
+            // Reset parameters to avoid them leaking or such.
+            myJobQueue = null;
+            myListener = null;
+
+            throw throwable;
+        }
+
+        // Set the progress to 100%, so that any statusbar graphics and 
similar can be updated correctly
+        updateProgress( 1, "Done" );
+
+        // Correct our predicted time for this type of job based on how long 
this one took
+        updatePrediction( getElapsedTime_s() );
+    }
+
+
+    public final void cancel()
+    {
+        myCanceled = true;
+
+        onCancel();
+    }
+
+    //======================================================================
+    // Protected Methods
+
+    //----------------------------------------------------------------------
+    // Abstract Protected Methods
+
+    /**
+     * Called when this job is canceled.  The implementation should clean up 
any reserved resources.
+     * <p/>
+     * Alternatively the job implementation can ignore this method and poll 
{@link #isCanceled} instead.
+     */
+    protected abstract void onCancel();
+
+    /**
+     * Called by the {@link AbstractJob#start} method.
+     * Should run the job, and return when it is ready.
+     *
+     * @throws Throwable thrown if there was some exception raised during the 
calculation.
+     *                   In such case, the job is canceled.
+     */
+    protected abstract void doJob() throws Throwable;
+
+    //----------------------------------------------------------------------
+    // Other Protected Methods
+
+    /**
+     * Cancels this job from the job queue it belongs to.
+     */
+    protected final void abortJob()
+    {
+        if ( myJobQueue == null )
+        {
+            throw new IllegalStateException( "abortJob called before 
AbstractJob.start has been called." );
+        }
+
+        myJobQueue.cancelJob( this );
+    }
+
+
+    /**
+     * Can be used to update the progress of the job.
+     *
+     * @param effortDone how ready the job is, the range is 0 = all work left, 
0.5 = half work done, to 1 = all work done.
+     * @param status     a user readable description of the current status of 
the job.
+     */
+    protected final void updateProgress( final double effortDone, final String 
status )
+    {
+        ParameterChecker.checkNormalNumberInRangeInclusive( effortDone, 
"effortDone", 0.0, 1.0 );
+
+        if ( myJobQueue == null )
+        {
+            throw new IllegalStateException( "onProgress called before 
AbstractJob.start has been called." );
+        }
+
+        if ( myListener != null )
+        {
+            final double elapsedTime_s = getElapsedTime_s();
+            final double estimatedTimeByPreviousTasks_s = getEstimatedTime_s( 
1 );
+            final double estimatedTimeByEffortDone_s = effortDone == 0 ? 
estimatedTimeByPreviousTasks_s : elapsedTime_s / effortDone;
+
+            // Average the estimation for time left that we get based on how 
long previous tasks have taken,
+            // and the estimate we get based on how long this task has taken 
and how complete it is.
+            final double estimatedTime_s = MathUtils.interpolate( 
EFFORT_DONE_ESTIMATION_WEIGHT,
+                                                                  
estimatedTimeByPreviousTasks_s,
+                                                                  
estimatedTimeByEffortDone_s );
+            final double estimatedProgress = MathUtils.interpolateClampDouble( 
elapsedTime_s, 0, estimatedTime_s, 0, 1 );
+
+            myListener.onProgress( myJobQueue,
+                                   this,
+                                   status,
+                                   effortDone,
+                                   estimatedProgress,
+                                   Math.max( 0, estimatedTime_s - 
elapsedTime_s ) );
+        }
+    }
+
+
+    /**
+     * @return true if the job was canceled, false if not.
+     */
+    protected boolean isCanceled()
+    {
+        return myCanceled;
+    }
+
+    //======================================================================
+    // Private Methods
+
+    private double getElapsedTime_s()
+    {
+        return NANOSECONDS_TO_SECONDS * ( System.nanoTime() - myStartTime );
+    }
+
+
+    private void updatePrediction( final double elapsedTime_s )
+    {
+        // Get weighted average of previous estimated duration for this type 
of task,
+        // with the observed duration of this task type
+        final double previousEstimatedTime_s = getEstimatedTime_s( 
elapsedTime_s );
+        final double estimatedTime_s = MathUtils.interpolate( 
PREDICTED_TIME_STABILITY_FACTOR,
+                                                              elapsedTime_s,
+                                                              
previousEstimatedTime_s );
+
+        // Store the new estimated duration
+        thePredictedTimes_s.put( getClass(), estimatedTime_s );
+    }
+
+
+    private double getEstimatedTime_s( final double defaultValue )
+    {
+        return getValue( thePredictedTimes_s,
+                         getClass(),
+                         defaultValue );
+    }
+
+
+    private static <K, V> V getValue( final Map<K, V> map, final K key, final 
V defaultValue )
+    {
+        V value = defaultValue;
+
+        if ( map.containsKey( key ) )
+        {
+            value = map.get( key );
+        }
+
+        return value;
+    }
+
+}


Property changes on: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/AbstractJob.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/CancelReason.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/CancelReason.java
                           (rev 0)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/CancelReason.java
   2007-10-04 23:26:45 UTC (rev 216)
@@ -0,0 +1,22 @@
+package org.skycastle.util.jobqueue;
+
+/**
+ * Describes the reason why a job was canceled.
+ */
+public enum CancelReason
+{
+    /**
+     * Client code canceled the job
+     */
+    CANCEL_CALLED,
+
+    /**
+     * Client code canceled all jobs
+     */
+    CANCEL_ALL_CALLED,
+    
+    /**
+     * An exception was thrown by the job.
+     */
+    EXCEPTION_THROWN,
+}


Property changes on: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/CancelReason.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/Job.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/Job.java
                            (rev 0)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/Job.java
    2007-10-04 23:26:45 UTC (rev 216)
@@ -0,0 +1,30 @@
+package org.skycastle.util.jobqueue;
+
+/**
+ * Something that can be executed in a separate worker thread, and that may be 
canceled durin execution.
+ * Also allows listening to the job progress.
+ */
+// IDEA: Might also support splitting the job into several sub-jobs that can 
be executed in parallel on different cores.
+public interface Job
+{
+    /**
+     * Starts a job, and returns when it is completed.
+     *
+     * @param jobQueue the jobQueue that this job was started in.
+     * @param listener a listener that can be notified about the progress of 
the job.
+     *
+     * @throws Throwable any exception thrown by the job should be caught, and 
the job canceled.
+     */
+    void start( JobQueue jobQueue, JobProgressListener listener ) throws 
Throwable;
+
+    /**
+     * Stops the job.  Called by the JobQueue, should not be called by client 
code directly.
+     * <p/>
+     * The job can stop at its earliest convenient time.
+     * For example, if it has a processing loop, the loop could check a cancel 
flag now and again.
+     * <p/>
+     * The job can also be canceled before it has even started.
+     */
+    void cancel();
+
+}


Property changes on: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/Job.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListener.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListener.java
                            (rev 0)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListener.java
    2007-10-04 23:26:45 UTC (rev 216)
@@ -0,0 +1,42 @@
+package org.skycastle.util.jobqueue;
+
+/**
+ * A listener that is notified about the progress, starting, and stopping of a 
job.
+ */
+public interface JobListener<J extends Job>
+        extends JobProgressListener<J>
+{
+    /**
+     * Called when the job was started by the {@link JobQueue}.
+     *
+     * @param jobQueue the jobQueue that is handling the job.
+     * @param job      the job that was started.
+     */
+    void onStarting( JobQueue<J> jobQueue, J job );
+
+    /**
+     * Called when the job was successfully finished.
+     * The listener can use this callback to extract result data from the job 
object.
+     *
+     * @param jobQueue the {@link JobQueue} the job was in.
+     * @param job      the job that finished.
+     */
+    void onDone( JobQueue<J> jobQueue, J job );
+
+    /**
+     * Called when a job was canceled, either by a call to cancel, or by the 
job itself,
+     * or because an exception was thrown by the start method of the job.
+     *
+     * @param jobQueue     the {@link JobQueue} the job was in.
+     * @param job          the job that was canceled.
+     * @param cancelReason the reason the job was canceled.
+     * @param explanation  a user readable reason for why the job was canceled.
+     * @param exception    an exception that caused the job to get canceled, 
or null if none.
+     */
+    void onCanceled( JobQueue<J> jobQueue,
+                     J job,
+                     CancelReason cancelReason,
+                     String explanation,
+                     Throwable exception );
+
+}


Property changes on: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListener.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListenerAdapter.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListenerAdapter.java
                             (rev 0)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListenerAdapter.java
     2007-10-04 23:26:45 UTC (rev 216)
@@ -0,0 +1,40 @@
+package org.skycastle.util.jobqueue;
+
+/**
+ * {@inheritDoc}
+ * <p/>
+ * An abstract adapter class that provides empty implementations for all 
methods.
+ */
+public abstract class JobListenerAdapter<J extends Job>
+        implements JobListener<J>
+{
+    public void onStarting( final JobQueue<J> jobQueue, final J job )
+    {
+
+    }
+
+    public void onDone( final JobQueue<J> jobQueue, final J job )
+    {
+
+    }
+
+    public void onCanceled( final JobQueue<J> jobQueue,
+                            final J job,
+                            final CancelReason cancelReason,
+                            final String explanation,
+                            final Throwable exception )
+    {
+
+    }
+
+    public void onProgress( final JobQueue<J> jobQueue,
+                            final J job,
+                            final String status,
+                            final float effortDone,
+                            final float estimatedProgress,
+                            final float estimatedTimeLeft_s )
+    {
+
+    }
+
+}


Property changes on: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListenerAdapter.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobProgressListener.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobProgressListener.java
                            (rev 0)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobProgressListener.java
    2007-10-04 23:26:45 UTC (rev 216)
@@ -0,0 +1,28 @@
+package org.skycastle.util.jobqueue;
+
+/**
+ * A listener that is notified about the progress of a job.
+ */
+public interface JobProgressListener<J extends Job>
+{
+    /**
+     * Called at regular intervalls while the job is being executed, to 
communoicate the status and progress of the job.
+     * May not be called at all, depending on the job implementation.
+     *
+     * @param jobQueue            the queue the job is being executed in.
+     * @param job                 the job that is being executed
+     * @param status              a textual status describing what is 
currently being done for the job.
+     * @param effortDone          how ready the job is, the range is 0 = all 
work left, 0.5 = half work done, to 1 = all work done.
+     * @param estimatedProgress   how far the job is, in terms of time.  0= 
just started, 0.5 = half the time left, 1 = no time left.
+     *                            May be based on timings of earlier runs of 
the same type of job.
+     * @param estimatedTimeLeft_s estimated number of seconds left before this 
job is finished.
+     *                            May be based on timings of earlier runs of 
the same type of job.
+     */
+    void onProgress( JobQueue<J> jobQueue,
+                     J job,
+                     String status,
+                     double effortDone,
+                     double estimatedProgress,
+                     double estimatedTimeLeft_s );
+    
+}


Property changes on: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobProgressListener.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueue.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueue.java
                               (rev 0)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueue.java
       2007-10-04 23:26:45 UTC (rev 216)
@@ -0,0 +1,43 @@
+package org.skycastle.util.jobqueue;
+
+/**
+ * Keeps track of a sequnce of jobs, and executes them in worker threads.
+ * <p/>
+ * Notifies callers when jobs are done.  Jobs can be canceled by calling the 
cancel method on a job,
+ * or all remaining jobs can be canceled at once with {@link #cancelAllJobs}.
+ */
+public interface JobQueue<J extends Job>
+{
+    /**
+     * @param job a {@link Job} to be executed by this job queue whenever it 
has time.
+     *            Should not be null, or already enqueued.
+     */
+    void enqueueJob( J job );
+
+    /**
+     * Cancels the specified {@link Job} and removes it from the job queue.
+     *
+     * @param job the {@link Job} to cancel.  Should not be null.
+     */
+    void cancelJob( J jobToRemove );
+
+    /**
+     * Cancels all jobs currently in this {@link JobQueue}.
+     */
+    void cancelAllJobs();
+
+
+    /**
+     * Adds the specified JobListener.
+     *
+     * @param addedJobListener should not be null or already added.
+     */
+    void addJobListener(JobListener<J> addedJobListener );
+
+    /**
+     * Removes the specified JobListener.
+     *
+     * @param removedJobListener should not be null.
+     */
+    void removeJobListener(JobListener<J> removedJobListener );
+}


Property changes on: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueue.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueueImpl.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueueImpl.java
                           (rev 0)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueueImpl.java
   2007-10-04 23:26:45 UTC (rev 216)
@@ -0,0 +1,305 @@
+package org.skycastle.util.jobqueue;
+
+import org.skycastle.util.ParameterChecker;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * {@inheritDoc}
+ */
+public final class JobQueueImpl<J extends Job>
+        implements JobQueue<J>
+{
+
+    //======================================================================
+    // Private Fields
+
+    /**
+     * The job queue.  Does not contain the currently executing job.
+     * Should always be syncronized using itself as a lock when accessed.
+     */
+    private final List<J> myJobs = new LinkedList<J>();
+
+    /**
+     * Listeners that are notified of job status changes and progress.  Should 
only be called in the swing/AWT event handling thread.
+     */
+    private final List<JobListener<J>> myListeners = new 
ArrayList<JobListener<J>>( 5 );
+
+    /**
+     * The currently executing job.  Should be syncronized using myJobs as a 
lock when accessed.
+     */
+    private J myCurrentJob = null;
+
+    /**
+     * Set to the currently job that was canceled.  Used to stop the currently 
executing job without stopping any
+     * future jobs if the currently executing job was already stopped.
+     */
+    private J myCanceledJob = null;
+
+    /**
+     * A job listener instance that forwards listner progress calls to the AWT 
event dispatching thread,
+     * and notifies each listener from it.
+     */
+    private final JobListener<J> myProxyListener = new JobListener<J>()
+    {
+
+        public void onStarting( final JobQueue<J> jobQueue, final J job )
+        {
+            // Notify listeners from the event dispatching thread
+            SwingUtilities.invokeLater( new Runnable()
+            {
+
+                public void run()
+                {
+                    for ( JobListener<J> listener : myListeners )
+                    {
+                        listener.onStarting( JobQueueImpl.this, job );
+                    }
+                }
+
+            } );
+        }
+
+
+        public void onDone( final JobQueue<J> jobQueue, final J job )
+        {
+            // Notify listeners from the event dispatching thread
+            SwingUtilities.invokeLater( new Runnable()
+            {
+
+                public void run()
+                {
+                    for ( JobListener<J> listener : myListeners )
+                    {
+                        listener.onDone( JobQueueImpl.this, job );
+                    }
+                }
+
+            } );
+        }
+
+
+        public void onCanceled( final JobQueue<J> jobQueue,
+                                final J job,
+                                final CancelReason cancelReason,
+                                final String explanation,
+                                final Throwable exception )
+        {
+            // Notify listeners from the event dispatching thread
+            SwingUtilities.invokeLater( new Runnable()
+            {
+
+                public void run()
+                {
+                    for ( JobListener<J> listener : myListeners )
+                    {
+                        listener.onCanceled( JobQueueImpl.this, job, 
cancelReason, explanation, exception );
+                    }
+                }
+
+            } );
+        }
+
+
+        public void onProgress( final JobQueue<J> jobQueue,
+                                final J job,
+                                final String status,
+                                final double effortDone,
+                                final double estimatedProgress,
+                                final double estimatedTimeLeft_s )
+        {
+            // Notify listeners from the event dispatching thread
+            SwingUtilities.invokeLater( new Runnable()
+            {
+
+                public void run()
+                {
+                    for ( JobListener<J> listener : myListeners )
+                    {
+                        listener.onProgress( JobQueueImpl.this, job, status, 
effortDone, estimatedProgress, estimatedTimeLeft_s );
+                    }
+                }
+
+            } );
+        }
+
+    };
+
+    //======================================================================
+    // Public Methods
+
+    //----------------------------------------------------------------------
+    // Constructors
+
+    /**
+     * Creates a new JobQueueImpl.  Starts a worker thread that will wait for 
enqueued {@link Job}s.
+     * The worker thread is automatically terminated when the application 
terminates.
+     */
+    public JobQueueImpl()
+    {
+        final JobQueue<J> queue = JobQueueImpl.this;
+
+        // Start a thread that handles texture painting jobs
+        final Thread workerThread = new Thread( new Runnable()
+        {
+
+            public void run()
+            {
+                while ( true )
+                {
+                    final J job = getNextJob();
+
+                    synchronized ( myJobs )
+                    {
+                        if ( job != myCanceledJob )
+                        {
+                            myProxyListener.onStarting( JobQueueImpl.this, job 
);
+
+                            // Run job
+                            try
+                            {
+                                job.start( queue, null );
+                            }
+                            catch ( Throwable throwable )
+                            {
+                                myProxyListener.onCanceled( JobQueueImpl.this,
+                                                            job,
+                                                            
CancelReason.EXCEPTION_THROWN,
+                                                            
throwable.getMessage(),
+                                                            throwable );
+                            }
+
+                            myProxyListener.onDone( JobQueueImpl.this, job );
+                        }
+
+                        myCanceledJob = null;
+                    }
+                }
+            }
+
+        } );
+
+        // Terminate the worker thread when the application terminates.
+        workerThread.setDaemon( true );
+
+        workerThread.start();
+    }
+
+    //----------------------------------------------------------------------
+    // JobQueue Implementation
+
+    public void enqueueJob( final J job )
+    {
+        ParameterChecker.checkNotNull( job, "job" );
+
+        synchronized ( myJobs )
+        {
+            ParameterChecker.checkNotAlreadyContained( job, myJobs, "myJobs" );
+
+            myJobs.add( job );
+            myJobs.notifyAll();
+        }
+    }
+
+
+    public void cancelJob( final J jobToRemove )
+    {
+        ParameterChecker.checkNotNull( jobToRemove, "jobToRemove" );
+
+        synchronized ( myJobs )
+        {
+            if ( myJobs.contains( jobToRemove ) )
+            {
+                myJobs.remove( jobToRemove );
+            }
+            else if ( jobToRemove.equals( myCurrentJob ) )
+            {
+                myCanceledJob = myCurrentJob;
+            }
+
+            jobToRemove.cancel();
+        }
+    }
+
+
+    public void cancelAllJobs()
+    {
+        synchronized ( myJobs )
+        {
+            for ( J job : myJobs )
+            {
+                job.cancel();
+            }
+            myJobs.clear();
+
+            if ( myCurrentJob != null )
+            {
+                myCanceledJob = myCurrentJob;
+
+                myCurrentJob.cancel();
+            }
+        }
+    }
+
+
+    public void addJobListener( JobListener<J> addedJobListener )
+    {
+        ParameterChecker.checkNotNull( addedJobListener, "addedJobListener" );
+        ParameterChecker.checkNotAlreadyContained( addedJobListener, 
myListeners, "myListeners" );
+
+        myListeners.add( addedJobListener );
+    }
+
+
+    public void removeJobListener( JobListener<J> removedJobListener )
+    {
+        ParameterChecker.checkNotNull( removedJobListener, 
"removedJobListener" );
+
+        myListeners.remove( removedJobListener );
+    }
+
+    //======================================================================
+    // Private Methods
+
+    /**
+     * @return Returns the next job.  Blocks until one is available.
+     */
+    private J getNextJob()
+    {
+        J job = null;
+
+        while ( job == null )
+        {
+            synchronized ( myJobs )
+            {
+                myCurrentJob = null;
+
+                while ( myJobs.isEmpty() )
+                {
+                    try
+                    {
+                        myJobs.wait();
+                    }
+                    catch ( InterruptedException e )
+                    {
+                        // Ignore
+                    }
+                }
+
+                if ( !myJobs.isEmpty() )
+                {
+                    job = myJobs.get( 0 );
+                    myJobs.remove( job );
+                }
+
+                myCurrentJob = job;
+            }
+        }
+
+        return job;
+    }
+
+}


Property changes on: 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueueImpl.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

Other related posts:

  • » [skycastle-commits] SF.net SVN: skycastle: [216] trunk/skycastle/modules/utils/src/main/java/ org/skycastle/util