View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.util;
20  
21  import java.util.concurrent.atomic.AtomicBoolean;
22  
23  
24  /* ------------------------------------------------------------ */
25  /** Iterating Callback.
26   * <p>This specialised callback is used when breaking up an
27   * asynchronous task into smaller asynchronous tasks.  A typical pattern
28   * is that a successful callback is used to schedule the next sub task, but 
29   * if that task completes quickly and uses the calling thread to callback
30   * the success notification, this can result in a growing stack depth.
31   * </p>
32   * <p>To avoid this issue, this callback uses an Atomicboolean to note 
33   * if the success callback has been called during the processing of a 
34   * sub task, and if so then the processing iterates rather than recurses.
35   * </p>
36   * <p>This callback is passed to the asynchronous handling of each sub
37   * task and a call the {@link #succeeded()} on this call back represents
38   * completion of the subtask.  Only once all the subtasks are completed is 
39   * the {@link Callback#succeeded()} method called on the {@link Callback} instance
40   * passed the the {@link #IteratingCallback(Callback)} constructor.</p>
41   *  
42   */
43  public abstract class IteratingCallback implements Callback
44  {
45      final AtomicBoolean _iterating = new AtomicBoolean();
46      final Callback _callback;
47      
48      
49      public IteratingCallback(Callback callback)
50      {
51          _callback=callback;
52      }
53      
54      /* ------------------------------------------------------------ */
55      /**
56       * Process a subtask.
57       * <p>Called by {@link #iterate()} to process a sub task of the overall task
58       * <p>
59       * @return True if the total task is complete. If false is returned
60       * then this Callback must be scheduled to receive either a call to 
61       * {@link #succeeded()} or {@link #failed(Throwable)}.
62       * @throws Exception
63       */
64      abstract protected boolean process() throws Exception;
65      
66      /* ------------------------------------------------------------ */
67      /** This method is called initially to start processing and 
68       * is then called by subsequent sub task success to continue
69       * processing.
70       */
71      public void iterate()
72      {
73          try
74          {
75              // Keep iterating as long as succeeded() is called during process()
76              while(_iterating.compareAndSet(false,true))
77              {
78                  // process and test if we are complete
79                  if (process())
80                  {
81                      _callback.succeeded();
82                      return;
83                  }
84              }
85          }
86          catch(Exception e)
87          {
88              _iterating.set(false);
89              _callback.failed(e);
90          }
91          finally
92          {
93              _iterating.set(false);
94          }
95      }
96      
97      
98      @Override
99      public void succeeded()
100     {
101         if (!_iterating.compareAndSet(true,false))
102             iterate();
103     }
104 
105     @Override
106     public void failed(Throwable x)
107     {
108         _callback.failed(x);
109     }
110 
111 }