Coverage Report - org.apache.commons.javaflow.Continuation
 
Classes in this File Line Coverage Branch Coverage Complexity
Continuation
0%
0/34
0%
0/12
2.385
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.javaflow;
 18  
 
 19  
 import java.io.Serializable;
 20  
 import org.apache.commons.javaflow.bytecode.StackRecorder;
 21  
 import org.apache.commons.javaflow.utils.ReflectionUtils;
 22  
 import org.apache.commons.logging.Log;
 23  
 import org.apache.commons.logging.LogFactory;
 24  
 
 25  
 /**
 26  
  * Snapshot of a thread execution state.
 27  
  *
 28  
  * <p>
 29  
  * A {@link Continuation} object is an immutable object that captures everything in
 30  
  * the Java stack. This includes
 31  
  * (1) current instruction pointer,
 32  
  * (2) return addresses, and
 33  
  * (3) local variables.
 34  
  *
 35  
  * <p>
 36  
  * <tt>Continuation</tt> objects are used to restore the captured execution states
 37  
  * later.
 38  
  *
 39  
  * @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
 40  
  * @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a>
 41  
  * @version CVS $Revision: 480487 $
 42  
  */
 43  
 public final class Continuation implements Serializable {
 44  
 
 45  0
     private static final Log log = LogFactory.getLog(Continuation.class);
 46  
     private static final long serialVersionUID = 2L;
 47  
     
 48  
     private final StackRecorder stackRecorder;
 49  
 
 50  
     /**
 51  
      * Create a new continuation, which continue a previous continuation.
 52  
      */
 53  0
     private Continuation( final StackRecorder pStackRecorder ) {
 54  0
         stackRecorder = pStackRecorder;
 55  0
     }
 56  
 
 57  
 
 58  
     /**
 59  
      * get the current context.
 60  
      *
 61  
      * <p>
 62  
      * This method returns the same context object given to {@link #startWith(Runnable, Object)}
 63  
      * or {@link #continueWith(Continuation, Object)}.
 64  
      *
 65  
      * <p>
 66  
      * A different context can be used for each run of a continuation, so
 67  
      * this mechanism can be used to associate some state with each execution.
 68  
      *
 69  
      * @return
 70  
      *      null if this method is invoked outside {@link #startWith(Runnable, Object)}
 71  
      *      or {@link #continueWith(Continuation, Object)} .
 72  
      */
 73  
     public static Object getContext() {
 74  0
         return StackRecorder.get().getContext();
 75  
     }
 76  
 
 77  
     /**
 78  
      * Creates a new {@link Continuation} object from the specified {@link Runnable}
 79  
      * object.
 80  
      *
 81  
      * <p>
 82  
      * Unlike the {@link #startWith(Runnable)} method, this method doesn't actually
 83  
      * execute the <tt>Runnable</tt> object. It will be executed when
 84  
      * it's {@link #continueWith(Continuation) continued}.
 85  
      * 
 86  
      * @return
 87  
      *      always return a non-null valid object.
 88  
      */
 89  
     public static Continuation startSuspendedWith( final Runnable pTarget ) {
 90  0
         return new Continuation(new StackRecorder(pTarget));
 91  
     }
 92  
 
 93  
     /**
 94  
      * Starts executing the specified {@link Runnable} object in an environment
 95  
      * that allows {@link Continuation#suspend()}.
 96  
      *
 97  
      * <p>
 98  
      * This is a short hand for <tt>startWith(target,null)</tt>.
 99  
      *
 100  
      * @see #startWith(Runnable, Object).
 101  
      */
 102  
     public static Continuation startWith( final Runnable pTarget ) {
 103  0
         return startWith(pTarget, null);
 104  
     }
 105  
 
 106  
     /**
 107  
      * Starts executing the specified {@link Runnable} object in an environment
 108  
      * that allows {@link Continuation#suspend()}.
 109  
      *
 110  
      * This method blocks until the continuation suspends or completes.
 111  
      *
 112  
      * @param pTarget
 113  
      *      The object whose <tt>run</tt> method will be executed.
 114  
      * @param pContext
 115  
      *      This value can be obtained from {@link #getContext()} until this method returns.
 116  
      *      Can be null.
 117  
      * @return
 118  
      *      If the execution completes and there's nothing more to continue, return null.
 119  
      *      Otherwise, the execution has been {@link #suspend() suspended}, in which case
 120  
      *      a new non-null continuation is returned.
 121  
      * @see #getContext()
 122  
      */
 123  
     public static Continuation startWith( final Runnable pTarget, final Object pContext ) {
 124  0
         if(pTarget == null) {
 125  0
             throw new IllegalArgumentException("target is null");
 126  
         }
 127  
 
 128  0
         log.debug("starting new flow from " + ReflectionUtils.getClassName(pTarget) + "/" + ReflectionUtils.getClassLoaderName(pTarget));
 129  
 
 130  0
         return continueWith(new Continuation(new StackRecorder(pTarget)), pContext);
 131  
     }
 132  
 
 133  
     /**
 134  
      * Resumes the execution of the specified continuation from where it's left off.
 135  
      *
 136  
      * <p>
 137  
      * This is a short hand for <tt>continueWith(resumed,null)</tt>.
 138  
      *
 139  
      * @see #continueWith(Continuation, Object)
 140  
      */
 141  
     public static Continuation continueWith(final Continuation pOldContinuation) {
 142  0
         return continueWith(pOldContinuation, null);
 143  
     }
 144  
 
 145  
     /**
 146  
      * Resumes the execution of the specified continuation from where it's left off
 147  
      * and creates a new continuation representing the new state.
 148  
      *
 149  
      * This method blocks until the continuation suspends or completes.
 150  
      *
 151  
      * @param pOldContinuation
 152  
      *      The resumed continuation to be executed. Must not be null.
 153  
      * @param pContext
 154  
      *      This value can be obtained from {@link #getContext()} until this method returns.
 155  
      *      Can be null.
 156  
      * @return
 157  
      *      If the execution completes and there's nothing more to continue, return null.
 158  
      *      Otherwise, the execution has been {@link #suspend() suspended}, in which case
 159  
      *      a new non-null continuation is returned.
 160  
      * @see #getContext()
 161  
      */
 162  
     public static Continuation continueWith(final Continuation pOldContinuation, final Object pContext) {
 163  0
         if(pOldContinuation == null) {
 164  0
             throw new IllegalArgumentException("continuation parameter must not be null.");
 165  
         }
 166  
 
 167  0
         log.debug("continueing with continuation " + ReflectionUtils.getClassName(pOldContinuation) + "/" + ReflectionUtils.getClassLoaderName(pOldContinuation));
 168  
 
 169  
         while(true) {
 170  
             try {
 171  0
                 StackRecorder pStackRecorder =
 172  
                     new StackRecorder(pOldContinuation.stackRecorder).execute(pContext);
 173  0
                 if(pStackRecorder == null) {
 174  0
                     return null;
 175  
                 } else {
 176  0
                     return new Continuation(pStackRecorder);
 177  
                 }
 178  0
             } catch (ContinuationDeath e) {
 179  0
                 if(e.mode.equals(ContinuationDeath.MODE_AGAIN))
 180  0
                     continue;       // re-execute immediately
 181  0
                 if(e.mode.equals(ContinuationDeath.MODE_EXIT))
 182  0
                     return null;    // no more thing to continue
 183  0
                 if(e.mode.equals(ContinuationDeath.MODE_CANCEL))
 184  0
                     return pOldContinuation;
 185  0
                 throw new IllegalStateException("Illegal mode "+e.mode);
 186  
             }
 187  
         }
 188  
     }
 189  
 
 190  
     public boolean isSerializable() {
 191  0
         return stackRecorder.isSerializable();
 192  
     }
 193  
     
 194  
     /**
 195  
      * Stops the running continuation.
 196  
      *
 197  
      * <p>
 198  
      * This method can be only called inside {@link #continueWith} or {@link #startWith} methods.
 199  
      * When called, the thread returns from the above methods with a new {@link Continuation}
 200  
      * object that captures the thread state.
 201  
      *
 202  
      * @throws IllegalStateException
 203  
      *      if this method is called outside the {@link #continueWith} or {@link #startWith} methods.
 204  
      */
 205  
     public static void suspend() {
 206  0
         StackRecorder.suspend();
 207  0
     }
 208  
 
 209  
     /**
 210  
      * Completes the execution of the running continuation.
 211  
      *
 212  
      * <p>
 213  
      * This method can be only called inside {@link #continueWith} or {@link #startWith} methods.
 214  
      * When called, the thread returns from the above methods with null,
 215  
      * indicating that there's nothing more to continue.
 216  
      *
 217  
      * <p>
 218  
      * This method is similiar to how {@link System#exit(int)} works for JVM.
 219  
      */
 220  
     public static void exit() {
 221  0
         throw new ContinuationDeath(ContinuationDeath.MODE_EXIT);
 222  
     }
 223  
 
 224  
     /**
 225  
      * Jumps to where the execution was resumed.
 226  
      *
 227  
      * <p>
 228  
      * This method can be only called inside {@link #continueWith} or {@link #startWith} methods.
 229  
      * When called, the execution jumps to where it was resumed
 230  
      * (if the execution has never resumed before, from the beginning
 231  
      * of {@link Runnable#run()}.)
 232  
      *
 233  
      * <p>
 234  
      * Consider the following example:
 235  
      *
 236  
      * <pre>
 237  
      * Continuation.suspend();
 238  
      * System.out.println("resumed");
 239  
      *
 240  
      * r = new Random().nextInt(5);
 241  
      * if(r!=0) {
 242  
      *   System.out.println("do it again");
 243  
      *   Continuation.again();
 244  
      * }
 245  
      *
 246  
      * System.out.println("done");
 247  
      * </pre>
 248  
      *
 249  
      * <p>
 250  
      * This program produces an output like this (the exact number of
 251  
      * 'do it again' depends on each execution, as it's random.)
 252  
      *
 253  
      * <pre>
 254  
      * resumed
 255  
      * do it again
 256  
      * resumed
 257  
      * do it again
 258  
      * resumed
 259  
      * do it again
 260  
      * resumed
 261  
      * done
 262  
      * </pre>
 263  
      *
 264  
      * <p>
 265  
      * The calling {@link Continuation#startWith(Runnable)} method and
 266  
      * {@link Continuation#continueWith(Continuation)} method does not
 267  
      * return when a program running inside uses this method.
 268  
      */
 269  
     public static void again() {
 270  0
         throw new ContinuationDeath(ContinuationDeath.MODE_AGAIN);
 271  
     }
 272  
 
 273  
     /**
 274  
      * Jumps to where the execution was resumed, and suspend execution.
 275  
      *
 276  
      * <p>
 277  
      * This method almost works like the {@link #again()} method,
 278  
      * but instead of re-executing, this method first suspends the execution.
 279  
      *
 280  
      * <p>
 281  
      * Therefore,
 282  
      * the calling {@link Continuation#startWith(Runnable)} method and
 283  
      * {@link Continuation#continueWith(Continuation)} method
 284  
      * return when a program running inside uses this method.
 285  
      */
 286  
     public static void cancel() {
 287  0
         throw new ContinuationDeath(ContinuationDeath.MODE_CANCEL);
 288  
     }
 289  
 
 290  
     public String toString() {
 291  0
         return "Continuation@" + hashCode() + "/" + ReflectionUtils.getClassLoaderName(this);
 292  
     }
 293  
 }