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.io.IOException;
22  import java.io.InterruptedIOException;
23  import java.util.concurrent.CancellationException;
24  import java.util.concurrent.Semaphore;
25  import java.util.concurrent.atomic.AtomicBoolean;
26  
27  /* ------------------------------------------------------------ */
28  /**
29   * A Callback for simple reusable conversion of an 
30   * asynchronous API to blocking.
31   * <p>
32   * To avoid late redundant calls to {@link #succeeded()} or {@link #failed(Throwable)} from
33   * interfering with later reuses of this class, the callback context is used to hold pass a phase indicated
34   * and only a single callback per phase is allowed.
35   * <p>
36   * A typical usage pattern is:
37   * <pre>
38   * public class MyClass
39   * {
40   *     BlockingCallback cb = new BlockingCallback();
41   *     
42   *     public void blockingMethod(Object args) throws Exception
43   *     {
44   *         asyncMethod(args,cb);
45   *         cb.block();
46   *     }
47   *     
48   *     public <C>void asyncMethod(Object args, Callback callback)
49   *     {
50   *         ...
51   *     }
52   *  }
53   */
54  public class BlockingCallback implements Callback
55  {
56      private static Throwable COMPLETED=new Throwable();
57      private final AtomicBoolean _done=new AtomicBoolean(false);
58      private final Semaphore _semaphore = new Semaphore(0);
59      private Throwable _cause;
60      
61      public BlockingCallback()
62      {}
63  
64      @Override
65      public void succeeded()
66      {
67          if (_done.compareAndSet(false,true))
68          {
69              _cause=COMPLETED;
70              _semaphore.release();
71          }
72      }
73  
74      @Override
75      public void failed(Throwable cause)
76      {
77          if (_done.compareAndSet(false,true))
78          {
79              _cause=cause;
80              _semaphore.release();
81          }
82      }
83  
84      /** Block until the FutureCallback is done or cancelled and 
85       * after the return leave in the state as if a {@link #reset()} had been
86       * done.
87       * This is useful for code that wants to repeatable use a FutureCallback to convert
88       * an asynchronous API to a blocking API. 
89       * @return
90       * @throws IOException
91       */
92      public void block() throws IOException
93      {
94          try
95          {
96              _semaphore.acquire();
97              if (_cause==COMPLETED)
98                  return;
99              if (_cause instanceof IOException)
100                 throw (IOException) _cause;
101             if (_cause instanceof CancellationException)
102                 throw (CancellationException) _cause;
103             throw new IOException(_cause);
104         }
105         catch (final InterruptedException e)
106         {
107             throw new InterruptedIOException(){{initCause(e);}};
108         }
109         finally
110         {
111             _done.set(false);
112             _cause=null;
113         }
114     }
115     
116     
117     @Override
118     public String toString()
119     {
120         return String.format("%s@%x{%b,%b}",BlockingCallback.class.getSimpleName(),hashCode(),_done.get(),_cause==COMPLETED);
121     }
122 
123 }