1 package com.atlassian.plugin.manager;
2
3 import com.atlassian.plugin.ModuleDescriptor;
4 import com.atlassian.plugin.Plugin;
5 import com.atlassian.plugin.PluginAccessor;
6 import com.atlassian.plugin.PluginController;
7 import com.atlassian.plugin.event.PluginEventListener;
8 import com.atlassian.plugin.event.PluginEventManager;
9 import com.atlassian.plugin.event.events.PluginDisabledEvent;
10 import com.atlassian.plugin.event.events.PluginEnabledEvent;
11 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
12 import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
13 import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
14 import com.atlassian.plugin.instrumentation.PluginSystemInstrumentation;
15 import com.atlassian.plugin.instrumentation.Timer;
16 import com.atlassian.plugin.predicate.EnabledModulePredicate;
17 import com.atlassian.plugin.predicate.ModuleDescriptorPredicate;
18 import com.atlassian.plugin.predicate.ModuleOfClassPredicate;
19 import com.google.common.base.Function;
20 import com.google.common.base.Predicate;
21 import com.google.common.cache.LoadingCache;
22
23 import java.util.Collection;
24 import java.util.List;
25
26 import static com.google.common.base.Preconditions.checkNotNull;
27 import static com.google.common.cache.CacheBuilder.newBuilder;
28 import static com.google.common.cache.CacheLoader.from;
29 import static com.google.common.collect.ImmutableList.copyOf;
30 import static com.google.common.collect.Iterables.concat;
31 import static com.google.common.collect.Iterables.filter;
32 import static com.google.common.collect.Iterables.transform;
33 import static java.util.concurrent.TimeUnit.SECONDS;
34
35
36
37
38
39 public final class EnabledModuleCachingPluginAccessor extends ForwardingPluginAccessor implements PluginAccessor {
40 private static final long DESCRIPTOR_TIMEOUT_SEC = Long.getLong("com.atlassian.plugin.descriptor.class.cache.timeout.sec", 30 * 60);
41 private static final long MODULE_TIMEOUT_SEC = Long.getLong("com.atlassian.plugin.module.class.cache.timeout.sec", 30 * 60);
42
43 private final SafeModuleExtractor safeModuleExtractor;
44
45 private final LoadingCache<Class<ModuleDescriptor<Object>>, List<ModuleDescriptor<Object>>> cacheByDescriptorClass =
46 newBuilder()
47 .expireAfterAccess(DESCRIPTOR_TIMEOUT_SEC, SECONDS)
48 .build(from(new ByModuleDescriptorClassCacheLoader()));
49
50 private final LoadingCache<Class<?>, List<ModuleDescriptor<Object>>> cacheByModuleClass =
51 newBuilder()
52 .expireAfterAccess(MODULE_TIMEOUT_SEC, SECONDS)
53 .build(from(new ByModuleClassCacheLoader()));
54
55 public EnabledModuleCachingPluginAccessor(final PluginAccessor delegate, final PluginEventManager pluginEventManager, final PluginController pluginController) {
56 super(delegate);
57 checkNotNull(pluginEventManager);
58
59 this.safeModuleExtractor = new SafeModuleExtractor(pluginController);
60
61
62
63 pluginEventManager.register(this);
64 }
65
66
67
68
69
70
71
72 @SuppressWarnings("unused")
73 @PluginEventListener
74 public void onPluginDisable(PluginDisabledEvent event) {
75
76 invalidateAll();
77 }
78
79
80
81
82 @SuppressWarnings("unused")
83 @PluginEventListener
84 public void onPluginEnable(PluginEnabledEvent event) {
85
86 invalidateAll();
87 }
88
89 @SuppressWarnings("unused")
90 @PluginEventListener
91 public void onPluginModuleEnabled(final PluginModuleEnabledEvent event) {
92
93
94
95
96
97
98
99 invalidateAll();
100 }
101
102 @SuppressWarnings("unused")
103 @PluginEventListener
104 public void onPluginModuleDisabled(final PluginModuleDisabledEvent event) {
105
106
107
108
109
110
111
112 invalidateAll();
113 }
114
115 @SuppressWarnings("unused")
116 @PluginEventListener
117 public void onPluginFrameworkShutdown(final PluginFrameworkShutdownEvent event) {
118 invalidateAll();
119 }
120
121 private void invalidateAll() {
122 cacheByDescriptorClass.invalidateAll();
123 cacheByModuleClass.invalidateAll();
124 }
125
126
127
128
129 private class ByModuleDescriptorClassCacheLoader implements Function<Class<ModuleDescriptor<Object>>, List<ModuleDescriptor<Object>>> {
130 public List<ModuleDescriptor<Object>> apply(final Class<ModuleDescriptor<Object>> moduleDescriptorClass) {
131 return delegate.getEnabledModuleDescriptorsByClass(moduleDescriptorClass);
132 }
133 }
134
135
136
137
138 private class ByModuleClassCacheLoader implements Function<Class, List<ModuleDescriptor<Object>>> {
139 @SuppressWarnings("unchecked")
140 public List<ModuleDescriptor<Object>> apply(final Class moduleClass) {
141 return getEnabledModuleDescriptorsByModuleClass(moduleClass);
142 }
143 }
144
145
146
147
148
149
150
151
152
153 @SuppressWarnings("unchecked")
154 @Override
155 public <D extends ModuleDescriptor<?>> List<D> getEnabledModuleDescriptorsByClass(final Class<D> descriptorClazz) {
156 try (Timer ignored = PluginSystemInstrumentation.instance().pullTimer("getEnabledModuleDescriptorsByClass")) {
157 final List<ModuleDescriptor<Object>> descriptors =
158 cacheByDescriptorClass.getUnchecked((Class<ModuleDescriptor<Object>>) descriptorClazz);
159 return (List<D>) descriptors;
160 }
161 }
162
163
164
165
166
167
168
169
170
171 @Override
172 public <M> List<M> getEnabledModulesByClass(final Class<M> moduleClass) {
173 try (Timer ignored = PluginSystemInstrumentation.instance().pullTimer("getEnabledModulesByClass")) {
174
175 return (List<M>) copyOf(safeModuleExtractor.getModules(cacheByModuleClass.getUnchecked(moduleClass)));
176 }
177 }
178
179
180
181
182
183
184
185
186 private <M> List<ModuleDescriptor<M>> getEnabledModuleDescriptorsByModuleClass(final Class<M> moduleClass) {
187 final ModuleOfClassPredicate<M> ofType = new ModuleOfClassPredicate<>(moduleClass);
188 final EnabledModulePredicate<M> enabled = new EnabledModulePredicate<>();
189 return copyOf(getModuleDescriptors(delegate.getEnabledPlugins(), new ModuleDescriptorPredicate<M>() {
190 public boolean matches(final ModuleDescriptor<? extends M> moduleDescriptor) {
191 return ofType.matches(moduleDescriptor) && enabled.matches(moduleDescriptor);
192 }
193 }));
194 }
195
196
197
198
199
200
201
202
203
204 private <M> Iterable<ModuleDescriptor<M>> getModuleDescriptors(final Collection<Plugin> plugins, final ModuleDescriptorPredicate<M> predicate) {
205
206
207 final Function<ModuleDescriptor<?>, ModuleDescriptor<M>> coercer = new Function<ModuleDescriptor<?>, ModuleDescriptor<M>>() {
208 public ModuleDescriptor<M> apply(final ModuleDescriptor<?> input) {
209 @SuppressWarnings("unchecked")
210 final ModuleDescriptor<M> result = (ModuleDescriptor<M>) input;
211 return result;
212 }
213 };
214
215
216 final Predicate<ModuleDescriptor<M>> adapter = new Predicate<ModuleDescriptor<M>>() {
217 public boolean apply(final ModuleDescriptor<M> input) {
218 return predicate.matches(input);
219 }
220 };
221
222
223 final Function<Plugin, Iterable<ModuleDescriptor<M>>> descriptorExtractor = new Function<Plugin, Iterable<ModuleDescriptor<M>>>() {
224 public Iterable<ModuleDescriptor<M>> apply(final Plugin plugin) {
225 return filter(transform(plugin.getModuleDescriptors(), coercer), adapter);
226 }
227 };
228
229
230 return concat(transform(plugins, descriptorExtractor));
231 }
232 }