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<MyObject> 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 }