View Javadoc
1   package com.atlassian.plugin;
2   
3   import com.atlassian.plugin.hostcontainer.HostContainer;
4   import com.atlassian.plugin.util.ClassLoaderUtils;
5   import io.atlassian.util.concurrent.CopyOnWriteMap;
6   import org.slf4j.Logger;
7   import org.slf4j.LoggerFactory;
8   
9   import java.util.ArrayList;
10  import java.util.Collections;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.Map.Entry;
14  
15  import static java.util.Collections.unmodifiableMap;
16  
17  /**
18   * Default implementation of a descriptor factory that allows filtering of
19   * descriptor keys
20   */
21  public class DefaultModuleDescriptorFactory implements ModuleDescriptorFactory {
22      private static Logger log = LoggerFactory.getLogger(DefaultModuleDescriptorFactory.class);
23  
24      private final Map<String, Class<? extends ModuleDescriptor>> moduleDescriptorClasses = CopyOnWriteMap.<String, Class<? extends ModuleDescriptor>>builder().stableViews()
25              .newHashMap();
26      private final List<String> permittedModuleKeys = new ArrayList<>();
27      private final HostContainer hostContainer;
28  
29      /**
30       * Instantiates a descriptor factory that uses the host container to create
31       * descriptors
32       *
33       * @param hostContainer The host container implementation for descriptor
34       *                      creation
35       * @since 2.2.0
36       */
37      public DefaultModuleDescriptorFactory(final HostContainer hostContainer) {
38          this.hostContainer = hostContainer;
39      }
40  
41      public Class<? extends ModuleDescriptor> getModuleDescriptorClass(final String type) {
42          return moduleDescriptorClasses.get(type);
43      }
44  
45      public ModuleDescriptor<?> getModuleDescriptor(final String type) throws PluginParseException {
46          if (shouldSkipModuleOfType(type)) {
47              return null;
48          }
49  
50          final Class<? extends ModuleDescriptor> moduleDescriptorClazz = getModuleDescriptorClass(type);
51  
52          if (moduleDescriptorClazz == null) {
53              throw new PluginParseException("Cannot find ModuleDescriptor class for plugin of type '" + type + "'.");
54          }
55  
56          return hostContainer.create(moduleDescriptorClazz);
57      }
58  
59      protected boolean shouldSkipModuleOfType(final String type) {
60          synchronized (permittedModuleKeys) {
61              return !permittedModuleKeys.isEmpty() && !permittedModuleKeys.contains(type);
62          }
63      }
64  
65      @SuppressWarnings("unused")
66      public void setModuleDescriptors(final Map<String, String> moduleDescriptorClassNames) {
67          for (final Entry<String, String> entry : moduleDescriptorClassNames.entrySet()) {
68              final Class<ModuleDescriptor<?>> descriptorClass = getClassFromEntry(entry);
69              if (descriptorClass != null) {
70                  addModuleDescriptor(entry.getKey(), descriptorClass);
71              }
72          }
73      }
74  
75      private <D extends ModuleDescriptor<?>> Class<D> getClassFromEntry(final Map.Entry<String, String> entry) {
76          if (shouldSkipModuleOfType(entry.getKey())) {
77              return null;
78          }
79  
80          try {
81              final Class<D> descriptorClass = ClassLoaderUtils.loadClass(entry.getValue(), getClass());
82  
83              if (!ModuleDescriptor.class.isAssignableFrom(descriptorClass)) {
84                  log.error("Configured plugin module descriptor class " + entry.getValue() + " does not inherit from ModuleDescriptor");
85                  return null;
86              }
87              return descriptorClass;
88          } catch (final ClassNotFoundException e) {
89              log.error("Unable to add configured plugin module descriptor " + entry.getKey() + ". Class not found: " + entry.getValue());
90              return null;
91          }
92      }
93  
94      public boolean hasModuleDescriptor(final String type) {
95          return moduleDescriptorClasses.containsKey(type);
96      }
97  
98      public void addModuleDescriptor(final String type, final Class<? extends ModuleDescriptor> moduleDescriptorClass) {
99          moduleDescriptorClasses.put(type, moduleDescriptorClass);
100     }
101 
102     public void removeModuleDescriptorForType(final String type) {
103         moduleDescriptorClasses.remove(type);
104     }
105 
106     protected final Map<String, Class<? extends ModuleDescriptor>> getDescriptorClassesMap() {
107         return unmodifiableMap(moduleDescriptorClasses);
108     }
109 
110     /**
111      * Sets the list of module keys that will be loaded. If this list is empty,
112      * then the factory will permit all recognised module types to load. This
113      * allows you to run the plugin system in a 'restricted mode'
114      *
115      * @param permittedModuleKeys List of (String) keys
116      */
117     public void setPermittedModuleKeys(List<String> permittedModuleKeys) {
118         if (permittedModuleKeys == null) {
119             permittedModuleKeys = Collections.emptyList();
120         }
121 
122         synchronized (this.permittedModuleKeys) {
123             // synced
124             this.permittedModuleKeys.clear();
125             this.permittedModuleKeys.addAll(permittedModuleKeys);
126         }
127     }
128 }