View Javadoc

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 }