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 net.jcip.annotations.ThreadSafe;
20  
21  import java.lang.ref.Reference;
22  import java.lang.ref.WeakReference;
23  import java.util.concurrent.Callable;
24  import java.util.concurrent.ExecutionException;
25  import java.util.concurrent.FutureTask;
26  
27  /**
28   * Lazily loaded reference that is not constructed until required. This class is used to maintain a
29   * reference to an object that is expensive to create and must be constructed once and once only.
30   * This reference behaves as though the <code>final</code> keyword has been used (you cannot reset
31   * it once it has been constructed).
32   * <p>
33   * Usage: clients need to implement the {@link #create()} method to return the object this reference
34   * will hold.
35   * <p>
36   * For instance:
37   * <p>
38   * 
39   * <pre>
40   * final LazyReference&lt;MyObject&gt; ref = new LazyReference() {
41   *     protected MyObject create() throws Exception {
42   *         // Do expensive object construction here
43   *         return new MyObject();
44   *     }
45   * };
46   * </pre>
47   * 
48   * Then call {@link #get()} to get a reference to the referenced object:
49   * 
50   * <pre>
51   * MyObject myLazyLoadedObject = ref.get()
52   * </pre>
53   * 
54   * NOTE: Interruption policy is that if you want to be cancellable while waiting for another thread
55   * to create the value, instead of calling {@link #get()} call {@link #getInterruptibly()}. However,
56   * If your {@link #create()} method is interrupted and throws an {@link InterruptedException}, it is
57   * treated as an application exception and will be the causal exception inside the runtime
58   * {@link InitializationException} that {@link #get()} or {@link #getInterruptibly()} throws and
59   * your {@link #create()} will not be called again.
60   * <p>
61   * Implementation note. This class extends {@link WeakReference} as {@link Reference} does not have
62   * a public constructor. WeakReference is preferable as it does not have any members and therefore
63   * doesn't increase the memory footprint. As we never pass a referent through to the super-class and
64   * override {@link #get()}, the garbage collection semantics of WeakReference are irrelevant. The
65   * referenced object will not become eligible for GC unless the object holding the reference to this
66   * object is collectible.
67   */
68  @ThreadSafe public abstract class LazyReference<T> extends WeakReference<T> {
69      private final FutureTask<T> future = new FutureTask<T>(new Callable<T>() {
70          public T call() throws Exception {
71              return create();
72          }
73      });
74  
75      public LazyReference() {
76          super(null);
77      }
78  
79      /**
80       * The object factory method, guaranteed to be called once and only once.
81       * 
82       * @return the object that {@link #get()} and {@link #getInterruptibly()} will return.
83       * @throws Exception if anything goes wrong, rethrown as an InitializationException from
84       *             {@link #get()} and {@link #getInterruptibly()}
85       */
86      protected abstract T create() throws Exception;
87  
88      /**
89       * Get the lazily loaded reference in a non-cancellable manner. If your <code>create()</code>
90       * method throws an Exception calls to <code>get()</code> will throw an InitializationException
91       * which wraps the previously thrown exception.
92       * 
93       * @return the object that {@link #create()} created.
94       * @throws InitializationException if the {@link #create()} method throws an exception. The
95       *             {@link InitializationException#getCause()} will contain the exception thrown by
96       *             the {@link #create()} method
97       */
98      @Override public final T get() {
99          boolean interrupted = false;
100         try {
101             while (true) {
102                 try {
103                     return getInterruptibly();
104                 }
105                 catch (final InterruptedException ignore) {
106                     // ignore and try again
107                     interrupted = true;
108                 }
109             }
110         }
111         finally {
112             if (interrupted) {
113                 Thread.currentThread().interrupt();
114             }
115         }
116     }
117 
118     /**
119      * Get the lazily loaded reference in a cancellable manner. If your <code>create()</code> method
120      * throws an Exception, calls to <code>get()</code> will throw a RuntimeException which wraps
121      * the previously thrown exception.
122      * 
123      * @return the object that {@link #create()} created.
124      * @throws InitializationException if the {@link #create()} method throws an exception. The
125      *             {@link InitializationException#getCause()} will contain the exception thrown by
126      *             the {@link #create()} method
127      * @throws InterruptedException If the calling thread is Interrupted while waiting for another
128      *             thread to create the value (if the creating thread is interrupted while blocking
129      *             on something, the {@link InterruptedException} will be thrown as the causal
130      *             exception of the {@link InitializationException} to everybody calling this
131      *             method).
132      */
133     public final T getInterruptibly() throws InterruptedException {
134         future.run();
135 
136         try {
137             return future.get();
138         }
139         catch (final ExecutionException e) {
140             throw new InitializationException(e);
141         }
142     }
143 
144     /**
145      * Has the {@link #create()} reference been initialized.
146      * 
147      * @return true if the task is complete
148      */
149     public boolean isInitialized() {
150         return future.isDone();
151     }
152 
153     /**
154      * Has the {@link #create()} reference been initialized.
155      * 
156      * @return true if the task is complete
157      */
158     public void cancel() {
159         future.cancel(true);
160     }
161 
162     /**
163      * If the factory {@link LazyReference#create()} method threw an exception, this wraps it.
164      */
165     public static class InitializationException extends RuntimeException {
166         private static final long serialVersionUID = 3638376010285456759L;
167 
168         InitializationException(final ExecutionException e) {
169             super((e.getCause() != null) ? e.getCause() : e);
170         }
171     }
172 }