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 Callback has succeeded or failed and 
85       * after the return leave in the state to allow reuse.
86       * This is useful for code that wants to repeatable use a FutureCallback to convert
87       * an asynchronous API to a blocking API. 
88       * @throws IOException if exception was caught during blocking, or callback was cancelled 
89       */
90      public void block() throws IOException
91      {
92          try
93          {
94              _semaphore.acquire();
95              if (_cause==COMPLETED)
96                  return;
97              if (_cause instanceof IOException)
98                  throw (IOException) _cause;
99              if (_cause instanceof CancellationException)
100                 throw (CancellationException) _cause;
101             throw new IOException(_cause);
102         }
103         catch (final InterruptedException e)
104         {
105             throw new InterruptedIOException(){{initCause(e);}};
106         }
107         finally
108         {
109             _done.set(false);
110             _cause=null;
111         }
112     }
113     
114     
115     @Override
116     public String toString()
117     {
118         return String.format("%s@%x{%b,%b}",BlockingCallback.class.getSimpleName(),hashCode(),_done.get(),_cause==COMPLETED);
119     }
120 
121 }