1 /*
2 * Copyright (c) 2002-2004
3 * All rights reserved.
4 */
5 package com.atlassian.plugin.servlet.util;
6
7 import java.util.concurrent.Callable;
8 import java.util.concurrent.ExecutionException;
9 import java.util.concurrent.FutureTask;
10 import java.util.concurrent.atomic.AtomicReference;
11
12 /**
13 * Thread-safe lock-less (see note) reference that is not constructed until required.
14 * This class is used to maintain a reference to an object that is expensive to
15 * create and must be constructed once and once only. Therefore this reference behaves
16 * as though the <code>final</code> keyword has been used (you cannot reset it once it
17 * has been constructed).
18 * <p/>
19 * When using this class you need to implement the {@link #create()} method to
20 * return the object this reference will hold.
21 * <p/>
22 * For instance:
23 * <pre>
24 * final LazyLoadedReference ref = new LazyLoadedReference()
25 * {
26 * protected Object create() throws Exception
27 * {
28 * // Do some useful object construction here
29 * return new MyObject();
30 * }
31 * };
32 * </pre>
33 * Then call to get a reference to the object:
34 * <pre>
35 * MyObject myLazyLoadedObject = (MyObject) ref.get()
36 * </pre>
37 * <p/>
38 * <strong>Note:</strong> Copied from JIRA com.atlassian.jira.util.concurrent.ThreadsafeLazyLoadedReference and
39 * modified to use generics and java.util.concurrent.
40 *
41 * @since 2.1.0
42 */
43 public abstract class LazyLoadedReference<V>
44 {
45 private final AtomicReference<FutureTask<V>> ref = new AtomicReference<FutureTask<V>>();
46
47 /**
48 * Get the lazily loaded reference. If your <code>create()</code> method
49 * throws an Exception, calls to <code>get()</code> will throw a RuntimeException
50 * which wraps the previously thrown exception.
51 */
52 public final V get()
53 {
54 FutureTask<V> future = ref.get();
55 if (future == null)
56 {
57 // create a Future
58 future = new FutureTask<V>(new Callable<V>()
59 {
60 public V call() throws Exception
61 {
62 return create();
63 }
64 });
65 // set the reference only if it is still null
66 ref.compareAndSet(null, future);
67
68 // get the future that ref now holds (may be different to above)
69 future = ref.get();
70
71 // Remember that the run method is potentially called more than once (by different threads)
72 // but the FutireTask implementation ensures that after the first time, all the others are ignored.
73 // So the create() method is only invoked once.
74 future.run();
75 }
76
77 // we guarantee to return a value. So if the InterruptedException is thrown we will
78 // set the interrupted flag and try to get the value again - hence the while loop
79 while (true)
80 {
81 try
82 {
83 return future.get();
84 }
85 catch (InterruptedException interruptAndTryAgain)
86 {
87 // if interrupted set our current thread to interrupted but continue trying to get the value
88 Thread.currentThread().interrupt();
89 }
90 catch (ExecutionException e)
91 {
92 if (e.getCause() != null)
93 {
94 throw new InitializationException(e.getCause());
95 }
96 else
97 {
98 throw new InitializationException(e);
99 }
100 }
101 }
102 }
103
104 /**
105 * The object factory method, guaranteed to be called once and only once.
106 * <p>
107 * protected abstract V create() throws Exception;
108 */
109 protected abstract V create() throws Exception;
110
111 /**
112 * The factory {@link LazyLoadedReference#create()} method threw an exception.
113 */
114 public static class InitializationException extends RuntimeException
115 {
116 InitializationException(Throwable t)
117 {
118 super(t);
119 }
120 }
121 }