View Javadoc

1   package com.atlassian.cache.compat;
2   
3   import com.atlassian.annotations.PublicApi;
4   import com.atlassian.cache.CacheManager;
5   import com.atlassian.cache.memory.MemoryCacheManager;
6   import com.atlassian.sal.api.component.ComponentLocator;
7   import com.atlassian.util.concurrent.LazyReference;
8   
9   /**
10   * A component that makes it possible to obtain a cache manager in a
11   * cross-product, cross-version way.
12   * <p>
13   * Your plugin should list an explicit dependency on the {@code atlassian-cache-compat} module, and define the
14   * {@code CacheManagerFactory} as a component.  This will bring with it transitive dependencies on
15   * {@code atlassian-cache-api} and {@code atlassian-cache-memory}.  When the plugin is installed in an application that
16   * provides suitable versions of these libraries, the implementation is inherited from the application and
17   * the cluster-safe version is returned by {@link #getCacheManager()}.  Otherwise, a local, memory-only
18   * implementation is substituted.
19   * </p><p>
20   * <strong>WARNING</strong>:  The inheritance of the host application's implementation relies on the resolution of
21   * the OSGi bundle dependency, and this means in turn that the plugin must not use a newer version of the compatibility
22   * library than the version of the API that the host application provides.  In other words, if compatibility library
23   * version <code>2.0.11</code> is used, then the host must provide <code>atlassian-cache-api 2.0.11</code> or later
24   * for it to work.
25   * </p>
26   *
27   * @since v2.0.10
28   */
29  @PublicApi
30  public class CacheManagerFactory
31  {
32      private final LazyReference<CacheManager> cacheManagerRef = new LazyReference<CacheManager>()
33      {
34          @Override
35          protected CacheManager create() throws Exception
36          {
37              final CacheManager cacheManager = getCacheManagerFromComponentLocator();
38              return (cacheManager != null) ? cacheManager : new MemoryCacheManager();
39          }
40      };
41  
42      /**
43       * Obtain a reference to the cache manager.
44       * <p>
45       * Plugin components that need to use the {@code CacheManager} should have the
46       * {@code CacheManagerFactory} injected and use this method to get it instead of injecting
47       * the {@code CacheManager} directly.
48       * </p>
49       *
50       * @return the appropriate {@code CacheManager} implementation.
51       */
52      public CacheManager getCacheManager()
53      {
54          return cacheManagerRef.get();
55      }
56  
57      /**
58       * Wraps the static call to {@link ComponentLocator#getComponent(Class)} so it can be mocked out.
59       * <p>
60       * There are three cases that we expect to encounter here:
61       * </p>
62       * <ol>
63       * <li><strong>The application does not provide {@code atlassian-cache-api} at all</strong> &mdash;
64       *     In this case, the {@code ComponentLocator} will not find any such component and will return
65       *     {@code null}.  The factory will create a {@link MemoryCacheManager} instance to use, instead.</li>
66       * <li><strong>The application provides an older version of {@code atlassian-cache-api} than is provided
67       *     referenced through this compatibility library</strong> &mdash;
68       *     In this case, the OSGi bundle resolution will use the version of the cache API that is bundled in
69       *     the plugin because the application-provided version does not satisfy the dependency.  When this
70       *     method asks the {@code ComponentLocator} for the {@code CacheManager} component, it will supply
71       *     its own {@code CacheManager} class, which has a different {@code ClassLoader} and therefore does
72       *     not match.  The {@code ComponentLocator} will return {@code null}, so this factory will create a
73       *     {@link MemoryCacheManager} instance to use, instead.</li>
74       * <li><strong>The application provides a version of {@code atlassian-cache-api} that is at or above the
75       *     the version referenced through this compatibility library</strong> &mdash;
76       *     In this case, the OSGi bundle resolution will drop the bundled version in favour of the application's
77       *     version, because it is sufficient to satisfy the dependency.  When this method asks the
78       *     {@code ComponentLocator} for the {@code CacheManager} component, it will supply the application's
79       *     {@code CacheManager} class, which will match.  The application's cluster-aware implementation is
80       *     returned by this factory.</li>
81       * </ol>
82       *
83       * @return the component retrieved from the {@code ComponentLocator}, or {@code null} if it was not provided.
84       */
85      CacheManager getCacheManagerFromComponentLocator()
86      {
87          return ComponentLocator.getComponent(CacheManager.class);
88      }
89  }