package com.atlassian.labs.speakeasy.util; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Iterables.unmodifiableIterable; import static java.util.Collections.singleton; import com.atlassian.plugin.ModuleDescriptor; import com.atlassian.plugin.PluginAccessor; import com.atlassian.plugin.event.PluginEventListener; import com.atlassian.plugin.event.PluginEventManager; import com.atlassian.plugin.event.events.PluginDisabledEvent; import com.atlassian.plugin.event.events.PluginModuleDisabledEvent; import com.atlassian.plugin.event.events.PluginModuleEnabledEvent; import com.google.common.base.Function; import java.util.concurrent.CopyOnWriteArraySet; /** * Tracks enabled plugin module descriptors, focusing on fast reads. Copied from plugins framework until JIRA updates * to 2.6 */ public class DefaultPluginModuleTracker<M, T extends ModuleDescriptor<M>> implements PluginModuleTracker<M, T> { private final PluginEventManager pluginEventManager; private final Class<T> moduleDescriptorClass; private final Customizer<M, T> pluginModuleTrackerCustomizer; private final CopyOnWriteArraySet<T> moduleDescriptors = new CopyOnWriteArraySet<T>(); private final ModuleTransformer<M, T> moduleTransformer = new ModuleTransformer<M, T>(); // // ctors // public DefaultPluginModuleTracker(final PluginAccessor pluginAccessor, final PluginEventManager pluginEventManager, final Class<T> moduleDescriptorClass) { this(pluginAccessor, pluginEventManager, moduleDescriptorClass, new NoOpPluginModuleTrackerCustomizer<M, T>()); } public DefaultPluginModuleTracker(final PluginAccessor pluginAccessor, final PluginEventManager pluginEventManager, final Class<T> moduleDescriptorClass, final Customizer<M, T> pluginModuleTrackerCustomizer) { this.pluginEventManager = pluginEventManager; this.moduleDescriptorClass = moduleDescriptorClass; this.pluginModuleTrackerCustomizer = pluginModuleTrackerCustomizer; pluginEventManager.register(this); addDescriptors(pluginAccessor.getEnabledModuleDescriptorsByClass(moduleDescriptorClass)); } // // PluginModuleTracker impl // public Iterable<T> getModuleDescriptors() { return unmodifiableIterable(moduleDescriptors); } public Iterable<M> getModules() { return transform(getModuleDescriptors(), moduleTransformer); } public int size() { return moduleDescriptors.size(); } public void close() { pluginEventManager.unregister(this); } // // plugin event listening // @PluginEventListener public void onPluginModuleEnabled(final PluginModuleEnabledEvent event) { addDescriptors(singleton((ModuleDescriptor<?>) event.getModule())); } @PluginEventListener public void onPluginModuleDisabled(final PluginModuleDisabledEvent event) { removeDescriptors(singleton((ModuleDescriptor<?>) event.getModule())); } @PluginEventListener public void onPluginDisabled(final PluginDisabledEvent event) { removeDescriptors(event.getPlugin().getModuleDescriptors()); } // // module descriptor management // private void addDescriptors(final Iterable<? extends ModuleDescriptor<?>> descriptors) { for (final T descriptor : filtered(descriptors)) { final T customized = pluginModuleTrackerCustomizer.adding(descriptor); if (customized != null) { moduleDescriptors.add(customized); } } } private void removeDescriptors(final Iterable<? extends ModuleDescriptor<?>> descriptors) { for (final T descriptor : filtered(descriptors)) { if (moduleDescriptors.remove(descriptor)) { pluginModuleTrackerCustomizer.removed(descriptor); } } } /** * The descriptors that match the supplied class. */ private Iterable<T> filtered(final Iterable<? extends ModuleDescriptor<?>> descriptors) { return filter(descriptors, moduleDescriptorClass); } // // inner classes // private static class NoOpPluginModuleTrackerCustomizer<M, T extends ModuleDescriptor<M>> implements PluginModuleTracker.Customizer<M, T> { public T adding(final T descriptor) { return descriptor; } public void removed(final T descriptor) {} } /** * Safely get the Module from a {@link ModuleDescriptor}. */ private static class ModuleTransformer<M, T extends ModuleDescriptor<M>> implements Function<T, M> { public M apply(final T from) { return from.getModule(); } } /** * Static factory method for constructing trackers generically where M is not known. * * @param <M> The module class, generically inferred. * @param <T> The module descriptor class. * @param pluginAccessor For getting the enabled descriptors of a certain type. * @param pluginEventManager For being told about changes to the enabled plugins. * @param moduleDescriptorClass The type of module descriptors we are interested in. * @return a PluginModuleTracker useful for fast and upd to date caching of the currently enabled module descriptors. * @since 2.7.0 */ public static <M, T extends ModuleDescriptor<M>> PluginModuleTracker<M, T> create(final PluginAccessor pluginAccessor, final PluginEventManager pluginEventManager, final Class<? extends ModuleDescriptor<?>> moduleDescriptorClass) { @SuppressWarnings("unchecked") final Class<T> klass = (Class<T>) moduleDescriptorClass; return new DefaultPluginModuleTracker<M, T>(pluginAccessor, pluginEventManager, klass); } }