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 }