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