View Javadoc

1   /**
2    * Copyright 2008 Atlassian Pty Ltd 
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0 
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package com.atlassian.util.concurrent;
18  
19  import java.lang.ref.Reference;
20  import java.lang.ref.WeakReference;
21  import java.util.concurrent.Callable;
22  import java.util.concurrent.ExecutionException;
23  import java.util.concurrent.FutureTask;
24  
25  import net.jcip.annotations.ThreadSafe;
26  
27  /**
28   * Lazily loaded reference that is not constructed until required. This class is
29   * used to maintain a reference to an object that is expensive to create and
30   * must be constructed once and once only. This reference behaves as though the
31   * <code>final</code> keyword has been used (you cannot reset it once it has
32   * been constructed).
33   * <p>
34   * Usage: clients need to implement the {@link #create()} method to return the
35   * object this reference will hold.
36   * <p>
37   * For instance:
38   * <p>
39   * 
40   * <pre>
41   * final LazyReference&lt;MyObject&gt; ref = new LazyReference() {
42   *     protected MyObject create() throws Exception {
43   *         // Do expensive object construction here
44   *         return new MyObject();
45   *     }
46   * };
47   * </pre>
48   * 
49   * Then call {@link #get()} to get a reference to the referenced object:
50   * 
51   * <pre>
52   * MyObject myLazyLoadedObject = ref.get()
53   * </pre>
54   * 
55   * NOTE: Interruption policy is that if you want to be cancellable while waiting
56   * for another thread to create the value, instead of calling {@link #get()}
57   * call {@link #getInterruptibly()}. However, If your {@link #create()} method
58   * is interrupted and throws an {@link InterruptedException}, it is treated as
59   * an application exception and will be the causal exception inside the runtime
60   * {@link InitializationException} that {@link #get()} or
61   * {@link #getInterruptibly()} throws and your {@link #create()} will not be
62   * called again.
63   * <p>
64   * Implementation note. This class extends {@link WeakReference} as
65   * {@link Reference} does not have a public constructor. WeakReference is
66   * preferable as it does not have any members and therefore doesn't increase the
67   * memory footprint. As we never pass a referent through to the super-class and
68   * override {@link #get()}, the garbage collection semantics of WeakReference
69   * are irrelevant. The referenced object will not become eligible for GC unless
70   * the object holding the reference to this object is collectible.
71   * 
72   * @param T the type of the contained element.
73   */
74  @ThreadSafe
75  public abstract class LazyReference<T> extends WeakReference<T> {
76      private final FutureTask<T> future = new FutureTask<T>(new Callable<T>() {
77          public T call() throws Exception {
78              return create();
79          }
80      });
81  
82      public LazyReference() {
83          super(null);
84      }
85  
86      /**
87       * The object factory method, guaranteed to be called once and only once.
88       * 
89       * @return the object that {@link #get()} and {@link #getInterruptibly()}
90       * will return.
91       * @throws Exception if anything goes wrong, rethrown as an
92       * InitializationException from {@link #get()} and
93       * {@link #getInterruptibly()}
94       */
95      protected abstract T create() throws Exception;
96  
97      /**
98       * Get the lazily loaded reference in a non-cancellable manner. If your
99       * <code>create()</code> method throws an Exception calls to
100      * <code>get()</code> will throw an InitializationException which wraps the
101      * previously thrown exception.
102      * 
103      * @return the object that {@link #create()} created.
104      * @throws InitializationException if the {@link #create()} method throws an
105      * exception. The {@link InitializationException#getCause()} will contain
106      * the exception thrown by the {@link #create()} method
107      */
108     @Override
109     public final T get() {
110         boolean interrupted = false;
111         try {
112             while (true) {
113                 try {
114                     return getInterruptibly();
115                 } catch (final InterruptedException ignore) {
116                     // ignore and try again
117                     interrupted = true;
118                 }
119             }
120         } finally {
121             if (interrupted) {
122                 Thread.currentThread().interrupt();
123             }
124         }
125     }
126 
127     /**
128      * Get the lazily loaded reference in a cancellable manner. If your
129      * <code>create()</code> method throws an Exception, calls to
130      * <code>get()</code> will throw a RuntimeException which wraps the
131      * previously thrown exception.
132      * 
133      * @return the object that {@link #create()} created.
134      * @throws InitializationException if the {@link #create()} method throws an
135      * exception. The {@link InitializationException#getCause()} will contain
136      * the exception thrown by the {@link #create()} method
137      * @throws InterruptedException If the calling thread is Interrupted while
138      * waiting for another thread to create the value (if the creating thread is
139      * interrupted while blocking on something, the {@link InterruptedException}
140      * will be thrown as the causal exception of the
141      * {@link InitializationException} to everybody calling this method).
142      */
143     public final T getInterruptibly() throws InterruptedException {
144         if (!future.isDone()) {
145             future.run();
146         }
147 
148         try {
149             return future.get();
150         } catch (final ExecutionException e) {
151             throw new InitializationException(e);
152         }
153     }
154 
155     /**
156      * Has the {@link #create()} reference been initialized.
157      * 
158      * @return true if the task is complete
159      */
160     public boolean isInitialized() {
161         return future.isDone();
162     }
163 
164     /**
165      * Cancel the initializing operation if it has not already run. Will try and
166      * interrupt if it is currently running.
167      */
168     public void cancel() {
169         future.cancel(true);
170     }
171 
172     /**
173      * If the factory {@link LazyReference#create()} method threw an exception,
174      * this wraps it.
175      */
176     public static class InitializationException extends RuntimeException {
177         private static final long serialVersionUID = 3638376010285456759L;
178 
179         InitializationException(final ExecutionException e) {
180             super((e.getCause() != null) ? e.getCause() : e);
181         }
182     }
183 }