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