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