[skycastle-commits] SF.net SVN: skycastle: [219] trunk/skycastle/modules/utils/src

  • From: zzorn@xxxxxxxxxxxxxxxxxxxxx
  • To: skycastle-commits@xxxxxxxxxxxxx
  • Date: Fri, 05 Oct 2007 12:35:12 -0700

Revision: 219
          http://skycastle.svn.sourceforge.net/skycastle/?rev=219&view=rev
Author:   zzorn
Date:     2007-10-05 12:35:12 -0700 (Fri, 05 Oct 2007)

Log Message:
-----------
Added test to the jobqueue, and fixed some issues that were revealed by the 
tests.

Modified Paths:
--------------
    
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/MathUtils.java
    
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/JobListenerAdapter.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

Added Paths:
-----------
    trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/jobqueue/
    
trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/jobqueue/TestJobQueue.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-10-04 23:28:15 UTC (rev 218)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/MathUtils.java   
    2007-10-05 19:35:12 UTC (rev 219)
@@ -64,7 +64,25 @@
         return value;
     }
 
+    /**
+     * @param value
+     *
+     * @return the input value, clamped to the 0..1 range.
+     */
+    public static double clampToZeroToOne( double value )
+    {
+        if ( value < 0 )
+        {
+            value = 0;
+        }
+        if ( value > 1 )
+        {
+            value = 1;
+        }
+        return value;
+    }
 
+
     /**
      * Clamps the given value to the -1..1 range.
      */

Modified: 
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
    2007-10-04 23:28:15 UTC (rev 218)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/AbstractJob.java
    2007-10-05 19:35:12 UTC (rev 219)
@@ -19,9 +19,12 @@
     //======================================================================
     // Private Fields
 
+    private final Object myCancelLock = new Object();
+
     private JobQueue myJobQueue = null;
     private JobProgressListener myListener = null;
     private boolean myCanceled = false;
+    private boolean myDone = false;
     private long myStartTime;
 
     //======================================================================
@@ -40,10 +43,28 @@
 
     public final void start( final JobQueue jobQueue, final 
JobProgressListener listener ) throws Throwable
     {
+        if ( myDone )
+        {
+            throw new IllegalArgumentException( "The job has already been done 
once, " +
+                                                "it is not possible to run the 
same job twice." );
+        }
+
         ParameterChecker.checkNotNull( jobQueue, "jobQueue" );
         myJobQueue = jobQueue;
         myListener = listener;
 
+        // Check if the job was canceled before it was started.
+        // If so, notify the job queue, and return from this method.
+        synchronized ( myCancelLock )
+        {
+            if ( myCanceled )
+            {
+                myJobQueue.cancelJob( this );
+
+                return;
+            }
+        }
+
         myStartTime = System.nanoTime();
 
         // Notify listeners about the initial estimated time, based on 
previous tasks of this type.
@@ -68,16 +89,45 @@
 
         // Correct our predicted time for this type of job based on how long 
this one took
         updatePrediction( getElapsedTime_s() );
+
+        myDone = true;
+
     }
 
 
     public final void cancel()
     {
-        myCanceled = true;
+        // Cancel can be called both from the event handling thread, as well 
as from the job itself,
+        // so syncronize the implementation so that a job doesn't get canceled 
twice.
+        synchronized ( myCancelLock )
+        {
+            if ( !myCanceled )
+            {
+                myCanceled = true;
 
-        onCancel();
+                if ( myJobQueue != null )
+                {
+                    myJobQueue.cancelJob( this );
+                }
+
+                onCancel();
+            }
+        }
     }
 
+    //----------------------------------------------------------------------
+    // Other Public Methods
+
+    /**
+     * @return true if the job has completed and the result is ready,
+     *         but the progress listeners have not necessarily yet been called.
+     *         False if the job is not started, or is still ongoing.
+     */
+    public final boolean isDone()
+    {
+        return myDone;
+    }
+
     //======================================================================
     // Protected Methods
 
@@ -104,20 +154,6 @@
     // 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.
@@ -148,8 +184,8 @@
             myListener.onProgress( myJobQueue,
                                    this,
                                    status,
-                                   effortDone,
-                                   estimatedProgress,
+                                   MathUtils.clampToZeroToOne( effortDone ),
+                                   MathUtils.clampToZeroToOne( 
estimatedProgress ),
                                    Math.max( 0, estimatedTime_s - 
elapsedTime_s ) );
         }
     }
@@ -158,11 +194,20 @@
     /**
      * @return true if the job was canceled, false if not.
      */
-    protected boolean isCanceled()
+    protected final boolean isCanceled()
     {
         return myCanceled;
     }
 
+
+    /**
+     * @return true if the job was not canceled, false if it was canceled.
+     */
+    protected final boolean isRunning()
+    {
+        return !myCanceled;
+    }
+
     //======================================================================
     // Private Methods
 

Modified: 
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
   2007-10-04 23:28:15 UTC (rev 218)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/CancelReason.java
   2007-10-05 19:35:12 UTC (rev 219)
@@ -6,16 +6,11 @@
 public enum CancelReason
 {
     /**
-     * Client code canceled the job
+     * Client code or the job itself canceled the job
      */
     CANCEL_CALLED,
 
     /**
-     * Client code canceled all jobs
-     */
-    CANCEL_ALL_CALLED,
-    
-    /**
      * An exception was thrown by the job.
      */
     EXCEPTION_THROWN,

Modified: 
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
    2007-10-04 23:28:15 UTC (rev 218)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/Job.java
    2007-10-05 19:35:12 UTC (rev 219)
@@ -18,12 +18,15 @@
     void start( JobQueue jobQueue, JobProgressListener listener ) throws 
Throwable;
 
     /**
-     * Stops the job.  Called by the JobQueue, should not be called by client 
code directly.
+     * Stops the job and removes it from the jobQueue.
      * <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.
+     * <p/>
+     * Implementations should make sure to call {@link JobQueue#cancelJob} to 
make sure the job is removed from the
+     * job queue also (or they can extend AbstractJob that takes care of it).
      */
     void cancel();
 

Modified: 
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
     2007-10-04 23:28:15 UTC (rev 218)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobListenerAdapter.java
     2007-10-05 19:35:12 UTC (rev 219)
@@ -30,9 +30,9 @@
     public void onProgress( final JobQueue<J> jobQueue,
                             final J job,
                             final String status,
-                            final float effortDone,
-                            final float estimatedProgress,
-                            final float estimatedTimeLeft_s )
+                            final double effortDone,
+                            final double estimatedProgress,
+                            final double estimatedTimeLeft_s )
     {
 
     }

Modified: 
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
       2007-10-04 23:28:15 UTC (rev 218)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueue.java
       2007-10-05 19:35:12 UTC (rev 219)
@@ -17,27 +17,28 @@
     /**
      * Cancels the specified {@link Job} and removes it from the job queue.
      *
-     * @param job the {@link Job} to cancel.  Should not be null.
+     * @param jobToRemove the {@link Job} to cancel.  Should not be null.
+     *
+     * @return true if the job was found and canceled, false if no such job 
was found.
      */
-    void cancelJob( J jobToRemove );
+    boolean 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 );
+    void addJobListener( JobListener<J> addedJobListener );
 
     /**
      * Removes the specified JobListener.
      *
      * @param removedJobListener should not be null.
      */
-    void removeJobListener(JobListener<J> removedJobListener );
+    void removeJobListener( JobListener<J> removedJobListener );
 }

Modified: 
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
   2007-10-04 23:28:15 UTC (rev 218)
+++ 
trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/jobqueue/JobQueueImpl.java
   2007-10-05 19:35:12 UTC (rev 219)
@@ -29,17 +29,6 @@
     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.
      */
@@ -128,6 +117,17 @@
 
     };
 
+    /**
+     * 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;
+
     //======================================================================
     // Public Methods
 
@@ -161,7 +161,7 @@
                             // Run job
                             try
                             {
-                                job.start( queue, null );
+                                job.start( queue, myProxyListener );
                             }
                             catch ( Throwable throwable )
                             {
@@ -205,7 +205,7 @@
     }
 
 
-    public void cancelJob( final J jobToRemove )
+    public boolean cancelJob( final J jobToRemove )
     {
         ParameterChecker.checkNotNull( jobToRemove, "jobToRemove" );
 
@@ -214,14 +214,22 @@
             if ( myJobs.contains( jobToRemove ) )
             {
                 myJobs.remove( jobToRemove );
+                cancelJobAndNotifyListner( jobToRemove );
             }
             else if ( jobToRemove.equals( myCurrentJob ) )
             {
                 myCanceledJob = myCurrentJob;
+                cancelJobAndNotifyListner( myCurrentJob );
             }
+            else
+            {
+                return false;
+            }
 
             jobToRemove.cancel();
         }
+
+        return true;
     }
 
 
@@ -231,15 +239,14 @@
         {
             for ( J job : myJobs )
             {
-                job.cancel();
+                cancelJobAndNotifyListner( job );
             }
             myJobs.clear();
 
             if ( myCurrentJob != null )
             {
                 myCanceledJob = myCurrentJob;
-
-                myCurrentJob.cancel();
+                cancelJobAndNotifyListner( myCurrentJob );
             }
         }
     }
@@ -264,6 +271,17 @@
     //======================================================================
     // Private Methods
 
+    private void cancelJobAndNotifyListner( final J canceledJob )
+    {
+        canceledJob.cancel();
+        myProxyListener.onCanceled( this,
+                                    canceledJob,
+                                    CancelReason.CANCEL_CALLED,
+                                    "Job canceled",
+                                    null );
+    }
+
+
     /**
      * @return Returns the next job.  Blocks until one is available.
      */

Added: 
trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/jobqueue/TestJobQueue.java
===================================================================
--- 
trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/jobqueue/TestJobQueue.java
                           (rev 0)
+++ 
trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/jobqueue/TestJobQueue.java
   2007-10-05 19:35:12 UTC (rev 219)
@@ -0,0 +1,324 @@
+package org.skycastle.util.jobqueue;
+
+import junit.framework.TestCase;
+
+/**
+ * {@inheritDoc}
+ */
+public class TestJobQueue
+        extends TestCase
+{
+
+    //======================================================================
+    // Private Fields
+
+    private JobQueue<CalculationJob> myJobQueue;
+    private CalculationJob myJob;
+    private TestJobQueue.MyJobListener myJobListener;
+
+    //======================================================================
+    // Public Methods
+
+    //----------------------------------------------------------------------
+    // Test Methods
+
+    public void testCancel() throws Exception
+    {
+        myJob.cancel();
+
+        myJobQueue.enqueueJob( myJob );
+
+        Thread.sleep( 500 );
+
+        assertTrue( "onCancel should have been called", 
myJobListener.isCancelCalled() );
+    }
+
+
+    public void testCancelSomeJobs() throws Exception
+    {
+        final CalculationJob job1 = new CalculationJob( "job 1" );
+        final CalculationJob job2 = new CalculationJob( "job 2" );
+        final CalculationJob job3 = new CalculationJob( "job 3" );
+        final CalculationJob job4 = new CalculationJob( "job 4" );
+
+        assertTrue( "Job 1 should not be done", !job1.isDone() );
+        assertTrue( "Job 2 should not be done", !job2.isDone() );
+        assertTrue( "Job 3 should not be done", !job3.isDone() );
+        assertTrue( "Job 4 should not be done", !job4.isDone() );
+
+
+        myJobQueue.enqueueJob( job1 );
+        myJobQueue.enqueueJob( job2 );
+        myJobQueue.enqueueJob( job3 );
+        myJobQueue.enqueueJob( job4 );
+
+        final boolean foundAndCanceled = myJobQueue.cancelJob( job2 );
+        assertTrue( "Job 2 should be found and canceled", foundAndCanceled );
+
+        job3.cancel();
+
+        Thread.sleep( 2000 );
+
+        assertTrue( "onDone should have been called", 
myJobListener.isDoneCalled() );
+        assertTrue( "onStarting should have been called", 
myJobListener.isStartingCalled() );
+        assertTrue( "onProgress should have been called", 
myJobListener.isProgressCaled() );
+        assertTrue( "onCancel should have been called", 
myJobListener.isCancelCalled() );
+
+/* Some of the jobs can get done before they are canceled - this is quite 
machine dependent, so commented out these tests for now.
+        assertTrue( "Job 1 should be done", job1.isDone() );
+        assertTrue( "Job 2 should not be done", !job2.isDone() );
+        assertTrue( "Job 3 should not be done", !job3.isDone() );
+        assertTrue( "Job 4 should be done", job4.isDone() );
+*/
+
+        assertTrue( "Job 1 should not be canceled", !job1.isCanceled() );
+        assertTrue( "Job 2 should be canceled", job2.isCanceled() );
+        assertTrue( "Job 3 should be canceled", job3.isCanceled() );
+        assertTrue( "Job 4 should not be canceled", !job4.isCanceled() );
+    }
+
+
+    public void testJobQueue() throws Exception
+    {
+        myJobQueue.enqueueJob( myJob );
+
+        Thread.sleep( 500 );
+
+        assertTrue( "onDone should have been called", 
myJobListener.isDoneCalled() );
+        assertTrue( "onStarting should have been called", 
myJobListener.isStartingCalled() );
+        assertTrue( "onProgress should have been called", 
myJobListener.isProgressCaled() );
+        assertFalse( "onCancel should not have been called", 
myJobListener.isCancelCalled() );
+        assertEquals( "Result should be correct", 101.0, 
myJobListener.getResult(), 0.001 );
+        assertEquals( "Result should be correct", 101.0, myJob.getResult(), 
0.001 );
+    }
+
+
+    public void testSeveralJobs() throws Exception
+    {
+        final CalculationJob job1 = new CalculationJob( "job 1" );
+        final CalculationJob job2 = new CalculationJob( "job 2" );
+        final CalculationJob job3 = new CalculationJob( "job 3" );
+        final CalculationJob job4 = new CalculationJob( "job 4" );
+
+        assertTrue( "Job 1 should not be done", !job1.isDone() );
+        assertTrue( "Job 2 should not be done", !job2.isDone() );
+        assertTrue( "Job 3 should not be done", !job3.isDone() );
+        assertTrue( "Job 4 should not be done", !job4.isDone() );
+
+        myJobQueue.enqueueJob( job1 );
+        myJobQueue.enqueueJob( job2 );
+        myJobQueue.enqueueJob( job3 );
+        myJobQueue.enqueueJob( job4 );
+
+        Thread.sleep( 2000 );
+
+        assertTrue( "onDone should have been called", 
myJobListener.isDoneCalled() );
+        assertTrue( "onStarting should have been called", 
myJobListener.isStartingCalled() );
+        assertTrue( "onProgress should have been called", 
myJobListener.isProgressCaled() );
+        assertFalse( "onCancel should not have been called", 
myJobListener.isCancelCalled() );
+        assertEquals( "Result should be correct", 101.0, 
myJobListener.getResult(), 0.001 );
+
+        assertTrue( "Job 1 should be done", job1.isDone() );
+        assertTrue( "Job 2 should be done", job2.isDone() );
+        assertTrue( "Job 3 should be done", job3.isDone() );
+        assertTrue( "Job 4 should be done", job4.isDone() );
+    }
+
+    //======================================================================
+    // Protected Methods
+
+    protected void setUp() throws Exception
+    {
+        myJobQueue = new JobQueueImpl<CalculationJob>();
+
+        myJobListener = new MyJobListener();
+        myJobQueue.addJobListener( myJobListener );
+
+        myJob = new CalculationJob( "calculation job" );
+    }
+
+    //======================================================================
+    // Inner Classes
+
+    private final class CalculationJob
+            extends AbstractJob
+    {
+
+        
//======================================================================
+        // Private Fields
+
+        private final String myName;
+
+        private double myResult;
+
+        
//======================================================================
+        // Private Constants
+
+        private static final int LOOP_COUNT = 100;
+
+        
//======================================================================
+        // Public Methods
+
+        
//----------------------------------------------------------------------
+        // Other Public Methods
+
+        public double getResult()
+        {
+            return myResult;
+        }
+
+        
//----------------------------------------------------------------------
+        // Caononical Methods
+
+        public String toString()
+        {
+            return myName;
+        }
+
+        
//======================================================================
+        // Protected Methods
+
+        protected void doJob() throws Throwable
+        {
+            double omm = 1;
+            for ( int i = 0; i < LOOP_COUNT && isRunning(); i++ )
+            {
+                final double oldOmm = omm;
+                omm += omm * omm;
+                omm /= oldOmm;
+
+                if ( i % 20 == 0 )
+                {
+                    updateProgress( ( 1.0 * i ) / ( 1.0 * LOOP_COUNT ), 
"Calculating Omm" );
+                }
+
+                Thread.sleep( 3 );
+            }
+
+
+            myResult = omm;
+        }
+
+
+        protected void onCancel()
+        {
+            // Nothing to do
+        }
+
+        
//======================================================================
+        // Private Methods
+
+        private CalculationJob( final String name )
+        {
+            myName = name;
+        }
+
+    }
+
+    private final class MyJobListener
+            extends JobListenerAdapter<CalculationJob>
+    {
+
+        
//======================================================================
+        // Private Fields
+
+        private double myResult;
+        private boolean myStartingCalled;
+        private boolean myDoneCalled;
+        private boolean myProgressCaled;
+        private boolean myCancelCalled;
+
+        
//======================================================================
+        // Public Methods
+
+        
//----------------------------------------------------------------------
+        // JobListener Implementation
+
+        public void onStarting( final JobQueue<CalculationJob> jobQueue, final 
CalculationJob job )
+        {
+            myStartingCalled = true;
+        }
+
+
+        public void onDone( final JobQueue<CalculationJob> jobQueue, final 
CalculationJob job )
+        {
+            myDoneCalled = true;
+            myResult = job.getResult();
+        }
+
+
+        public void onCanceled( final JobQueue<CalculationJob> jobQueue,
+                                final CalculationJob job,
+                                final CancelReason cancelReason,
+                                final String explanation,
+                                final Throwable exception )
+        {
+            myCancelCalled = true;
+        }
+
+        
//----------------------------------------------------------------------
+        // JobProgressListener Implementation
+
+        public void onProgress( final JobQueue<CalculationJob> jobQueue,
+                                final CalculationJob job,
+                                final String status,
+                                final double effortDone,
+                                final double estimatedProgress,
+                                final double estimatedTimeLeft_s )
+        {
+            myProgressCaled = true;
+        }
+
+        
//----------------------------------------------------------------------
+        // Other Public Methods
+
+        public final void reset()
+        {
+            myResult = 1;
+            myStartingCalled = false;
+            myDoneCalled = false;
+            myProgressCaled = false;
+            myCancelCalled = false;
+        }
+
+
+        public double getResult()
+        {
+            return myResult;
+        }
+
+
+        public boolean isStartingCalled()
+        {
+            return myStartingCalled;
+        }
+
+
+        public boolean isDoneCalled()
+        {
+            return myDoneCalled;
+        }
+
+
+        public boolean isProgressCaled()
+        {
+            return myProgressCaled;
+        }
+
+
+        public boolean isCancelCalled()
+        {
+            return myCancelCalled;
+        }
+
+        
//======================================================================
+        // Private Methods
+
+        private MyJobListener()
+        {
+            reset();
+        }
+
+    }
+
+}


Property changes on: 
trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/jobqueue/TestJobQueue.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: [219] trunk/skycastle/modules/utils/src