View Javadoc
1   package com.atlassian.plugin.tracker;
2   
3   import com.atlassian.plugin.ModuleDescriptor;
4   import com.atlassian.plugin.PluginAccessor;
5   import com.atlassian.plugin.event.PluginEventListener;
6   import com.atlassian.plugin.event.PluginEventManager;
7   import com.atlassian.plugin.event.events.PluginDisabledEvent;
8   import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
9   import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
10  import com.google.common.base.Function;
11  
12  import java.util.concurrent.CopyOnWriteArraySet;
13  
14  import static com.google.common.collect.Iterables.filter;
15  import static com.google.common.collect.Iterables.transform;
16  import static com.google.common.collect.Iterables.unmodifiableIterable;
17  import static java.util.Collections.singleton;
18  
19  /**
20   * Tracks enabled plugin module descriptors, focusing on fast reads
21   *
22   * @since 2.6.0
23   */
24  public class DefaultPluginModuleTracker<M, T extends ModuleDescriptor<M>> implements PluginModuleTracker<M, T> {
25      private final PluginEventManager pluginEventManager;
26      private final Class<T> moduleDescriptorClass;
27      private final Customizer<M, T> pluginModuleTrackerCustomizer;
28      private final CopyOnWriteArraySet<T> moduleDescriptors = new CopyOnWriteArraySet<>();
29      private final ModuleTransformer<M, T> moduleTransformer = new ModuleTransformer<>();
30  
31      //
32      // ctors
33      //
34  
35      public DefaultPluginModuleTracker(final PluginAccessor pluginAccessor, final PluginEventManager pluginEventManager,
36                                        final Class<T> moduleDescriptorClass) {
37          this(pluginAccessor, pluginEventManager, moduleDescriptorClass, new NoOpPluginModuleTrackerCustomizer<>());
38      }
39  
40      public DefaultPluginModuleTracker(final PluginAccessor pluginAccessor, final PluginEventManager pluginEventManager,
41                                        final Class<T> moduleDescriptorClass,
42                                        final Customizer<M, T> pluginModuleTrackerCustomizer) {
43          this.pluginEventManager = pluginEventManager;
44          this.moduleDescriptorClass = moduleDescriptorClass;
45          this.pluginModuleTrackerCustomizer = pluginModuleTrackerCustomizer;
46  
47          // TODO: registering an event handler in constructor isn't reliable:
48          //       if we're invoked in another thread before we're fully constructed, the event handler might throw exceptions.
49          //       Won't break the plugin manager (event publisher catches Throwable and logs) but we may miss that event.
50          // https://extranet.atlassian.com/display/CONF/2015/04/28/Publishing+unconstructed+objects+into+main+memory+considered+harmful
51          pluginEventManager.register(this);
52  
53          addDescriptors(pluginAccessor.getEnabledModuleDescriptorsByClass(moduleDescriptorClass));
54      }
55  
56      //
57      // PluginModuleTracker impl
58      //
59  
60      public Iterable<T> getModuleDescriptors() {
61          return unmodifiableIterable(moduleDescriptors);
62      }
63  
64      public Iterable<M> getModules() {
65          return transform(getModuleDescriptors(), moduleTransformer);
66      }
67  
68      public int size() {
69          return moduleDescriptors.size();
70      }
71  
72      public void close() {
73          pluginEventManager.unregister(this);
74      }
75  
76      //
77      // plugin event listening
78      //
79  
80      @PluginEventListener
81      public void onPluginModuleEnabled(final PluginModuleEnabledEvent event) {
82          addDescriptors(singleton((ModuleDescriptor<?>) event.getModule()));
83      }
84  
85      @PluginEventListener
86      public void onPluginModuleDisabled(final PluginModuleDisabledEvent event) {
87          removeDescriptors(singleton((ModuleDescriptor<?>) event.getModule()));
88      }
89  
90      @PluginEventListener
91      public void onPluginDisabled(final PluginDisabledEvent event) {
92          removeDescriptors(event.getPlugin().getModuleDescriptors());
93      }
94  
95      //
96      // module descriptor management
97      //
98  
99      private void addDescriptors(final Iterable<? extends ModuleDescriptor<?>> descriptors) {
100         for (final T descriptor : filtered(descriptors)) {
101             final T customized = pluginModuleTrackerCustomizer.adding(descriptor);
102             if (customized != null) {
103                 moduleDescriptors.add(customized);
104             }
105         }
106     }
107 
108     private void removeDescriptors(final Iterable<? extends ModuleDescriptor<?>> descriptors) {
109         for (final T descriptor : filtered(descriptors)) {
110             if (moduleDescriptors.remove(descriptor)) {
111                 pluginModuleTrackerCustomizer.removed(descriptor);
112             }
113         }
114     }
115 
116     /**
117      * The descriptors that match the supplied class.
118      */
119     private Iterable<T> filtered(final Iterable<? extends ModuleDescriptor<?>> descriptors) {
120         return filter(descriptors, moduleDescriptorClass);
121     }
122 
123     //
124     // inner classes
125     //
126 
127     private static class NoOpPluginModuleTrackerCustomizer<M, T extends ModuleDescriptor<M>> implements PluginModuleTracker.Customizer<M, T> {
128         public T adding(final T descriptor) {
129             return descriptor;
130         }
131 
132         public void removed(final T descriptor) {
133         }
134     }
135 
136     /**
137      * Safely get the Module from a {@link ModuleDescriptor}.
138      */
139     private static class ModuleTransformer<M, T extends ModuleDescriptor<M>> implements Function<T, M> {
140         public M apply(final T from) {
141             return from.getModule();
142         }
143     }
144 
145     /**
146      * Static factory method for constructing trackers generically where M is not known.
147      *
148      * @param <M>                   The module class, generically inferred.
149      * @param <T>                   The module descriptor class.
150      * @param pluginAccessor        For getting the enabled descriptors of a certain type.
151      * @param pluginEventManager    For being told about changes to the enabled plugins.
152      * @param moduleDescriptorClass The type of module descriptors we are interested in.
153      * @return a PluginModuleTracker useful for fast and upd to date caching of the currently enabled module descriptors.
154      * @since 2.7.0
155      */
156     public static <M, T extends ModuleDescriptor<M>> PluginModuleTracker<M, T> create(final PluginAccessor pluginAccessor,
157                                                                                       final PluginEventManager pluginEventManager,
158                                                                                       final Class<? extends ModuleDescriptor<?>> moduleDescriptorClass) {
159         @SuppressWarnings("unchecked")
160         final Class<T> klass = (Class<T>) moduleDescriptorClass;
161         return new DefaultPluginModuleTracker<>(pluginAccessor, pluginEventManager, klass);
162     }
163 }