View Javadoc

1   package com.atlassian.plugin.descriptors;
2   
3   import com.atlassian.plugin.*;
4   import com.atlassian.plugin.elements.ResourceLocation;
5   import com.atlassian.plugin.elements.ResourceDescriptor;
6   import com.atlassian.plugin.loaders.LoaderUtils;
7   import com.atlassian.plugin.util.JavaVersionUtils;
8   import org.dom4j.Element;
9   
10  import java.lang.reflect.Constructor;
11  import java.util.List;
12  import java.util.Map;
13  
14  public abstract class AbstractModuleDescriptor<T> implements ModuleDescriptor<T>, StateAware
15  {
16      protected Plugin plugin;
17      String key;
18      String name;
19      String moduleClassName;
20      Class moduleClass;
21      String description;
22      boolean enabledByDefault = true;
23      boolean systemModule = false;
24      protected boolean singleton = true;
25      Map<String,String> params;
26      protected Resources resources = Resources.EMPTY_RESOURCES;
27      private Float minJavaVersion;
28      private String i18nNameKey;
29      private String descriptionKey;
30  	private String completeKey;
31      boolean enabled = false;
32  
33      public void init(Plugin plugin, Element element) throws PluginParseException
34      {
35          this.plugin = plugin;
36          this.key = element.attributeValue("key");
37          this.name = element.attributeValue("name");
38          this.i18nNameKey = element.attributeValue("i18n-name-key");
39          this.completeKey = buildCompleteKey(plugin, key);
40          this.description = element.elementTextTrim("description");
41          this.moduleClassName = element.attributeValue("class");
42          Element descriptionElement = element.element("description");
43          this.descriptionKey = (descriptionElement != null) ? descriptionElement.attributeValue("key") : null;
44          params = LoaderUtils.getParams(element);
45  
46          if ("disabled".equalsIgnoreCase(element.attributeValue("state")))
47          {
48              enabledByDefault = false;
49          }
50  
51          if ("true".equalsIgnoreCase(element.attributeValue("system")))
52          {
53              systemModule = true;
54          }
55  
56          if (element.element("java-version") != null)
57          {
58              minJavaVersion = Float.valueOf(element.element("java-version").attributeValue("min"));
59          }
60  
61          if ("false".equalsIgnoreCase(element.attributeValue("singleton")))
62          {
63              singleton = false;
64          }
65          else if ("true".equalsIgnoreCase(element.attributeValue("singleton")))
66          {
67              singleton = true;
68          }
69          else
70          {
71              singleton = isSingletonByDefault();
72          }
73  
74          resources = Resources.fromXml(element);
75      }
76  
77      /**
78       * Override this for module descriptors which don't expect to be able to load a class successfully
79       * @param plugin
80       * @param element
81       * @deprecated Since 2.1.0, use {@link #loadClass(Plugin,String)} instead
82       */
83      protected void loadClass(Plugin plugin, Element element) throws PluginParseException {
84          loadClass(plugin, element.attributeValue("class"));
85      }
86  
87      /**
88       * Override this for module descriptors which don't expect to be able to load a class successfully
89       * @param plugin
90       * @param clazz The module class name to load
91       * @since 2.1.0
92       */
93      protected void loadClass(Plugin plugin, String clazz) throws PluginParseException {
94          try
95          {
96              if (clazz != null)  //not all plugins have to have a class
97              {
98                  // First try and load the class, to make sure the class exists
99                  moduleClass = plugin.loadClass(clazz, getClass());
100 
101                 // Then instantiate the class, so we can see if there are any dependencies that aren't satisfied
102                 try
103                 {
104                     Constructor noargConstructor = moduleClass.getConstructor(new Class[]{});
105                     if(noargConstructor != null)
106                     {
107                         moduleClass.newInstance();
108                     }
109                 }
110                 catch (NoSuchMethodException e)
111                 {
112                     // If there is no "noarg" constructor then don't do the check
113                 }
114             }
115         }
116         catch (ClassNotFoundException e)
117         {
118             throw new PluginParseException("Could not load class: " + clazz, e);
119         }
120         catch (NoClassDefFoundError e)
121         {
122             throw new PluginParseException("Error retrieving dependency of class: " + clazz + ". Missing class: " + e.getMessage());
123         }
124         catch (UnsupportedClassVersionError e)
125         {
126             throw new PluginParseException("Class version is incompatible with current JVM: " + clazz, e);
127         }
128         catch (Throwable t)
129         {
130             throw new PluginParseException(t);
131         }
132     }
133 
134     /**
135      * Build the complete key based on the provided plugin and module key. This method has no
136      * side effects.
137      * @param plugin The plugin for which the module belongs
138      * @param moduleKey The key for the module
139      * @return A newly constructed complete key, null if the plugin is null
140      */
141     private String buildCompleteKey(Plugin plugin, String moduleKey)
142     {
143         if (plugin == null)
144             return null;
145 
146         final StringBuffer completeKeyBuffer = new StringBuffer(32);
147         completeKeyBuffer.append(plugin.getKey()).append(":").append(moduleKey);
148         return completeKeyBuffer.toString();
149     }
150 
151     /**
152      * Override this if your plugin needs to clean up when it's been removed.
153      */
154     public void destroy(Plugin plugin)
155     {}
156 
157     public boolean isEnabledByDefault()
158     {
159         return enabledByDefault && satisfiesMinJavaVersion();
160     }
161 
162     public boolean isSystemModule()
163     {
164         return systemModule;
165     }
166 
167     public boolean isSingleton()
168     {
169         return singleton;
170     }
171 
172     /**
173      * Override this method if you want your module descriptor to be or not be a singleton by default.
174      * <p>
175      * Default is "true" - ie all plugin modules are singletons by default.
176      */
177     protected boolean isSingletonByDefault()
178     {
179         return true;
180     }
181 
182     /**
183      * Check that the module class of this descriptor implements a given interface, or extends a given class.
184      * @param requiredModuleClazz The class this module's class must implement or extend.
185      * @throws PluginParseException If the module class does not implement or extend the given class.
186      */
187     final protected void assertModuleClassImplements(Class requiredModuleClazz) throws PluginParseException
188     {
189         if (!enabled)
190         {
191             throw new PluginParseException("Plugin module " + getKey() + " not enabled");
192         }
193         if (!requiredModuleClazz.isAssignableFrom(getModuleClass()))
194         {
195             throw new PluginParseException("Given module class: " + getModuleClass().getName() + " does not implement " + requiredModuleClazz.getName());
196         }
197     }
198 
199     public String getCompleteKey() 
200 	{
201         return completeKey;
202     }
203 
204     public String getPluginKey()
205     {
206         return plugin.getKey();
207     }
208 
209     public String getKey()
210     {
211         return key;
212     }
213 
214     public String getName()
215     {
216         return name;
217     }
218 
219     public Class<T> getModuleClass()
220     {
221         return moduleClass;
222     }
223 
224     public abstract T getModule();
225 
226     public String getDescription()
227     {
228         return description;
229     }
230 
231     public Map getParams()
232     {
233         return params;
234     }
235 
236     public String getI18nNameKey()
237     {
238         return i18nNameKey;  //To change body of implemented methods use File | Settings | File Templates.
239     }
240 
241     public String getDescriptionKey()
242     {
243         return descriptionKey;  //To change body of implemented methods use File | Settings | File Templates.
244     }
245 
246     public List<ResourceDescriptor> getResourceDescriptors()
247     {
248         return resources.getResourceDescriptors();
249     }
250 
251     public List<ResourceDescriptor> getResourceDescriptors(String type)
252     {
253         return resources.getResourceDescriptors(type);
254     }
255 
256     public ResourceLocation getResourceLocation(String type, String name)
257     {
258         return resources.getResourceLocation(type, name);
259     }
260 
261     /**
262      * @deprecated
263      */
264     public ResourceDescriptor getResourceDescriptor(String type, String name)
265     {
266         return resources.getResourceDescriptor(type, name);
267     }
268 
269     public Float getMinJavaVersion()
270     {
271         return minJavaVersion;
272     }
273 
274     public boolean satisfiesMinJavaVersion()
275     {
276         if(minJavaVersion != null)
277         {
278             return JavaVersionUtils.satisfiesMinVersion(minJavaVersion);
279         }
280         return true;
281     }
282 
283     /**
284      * Sets the plugin for the ModuleDescriptor
285      */
286     public void setPlugin(Plugin plugin)
287     {
288         this.completeKey = buildCompleteKey(plugin, key);
289         this.plugin = plugin;
290     }
291 
292     /**
293      * @return The plugin this module descriptor is associated with
294      */
295     public Plugin getPlugin()
296     {
297         return plugin;
298     }
299 
300     public String toString()
301     {
302         return getCompleteKey() + " (" + getDescription() + ")";
303     }
304 
305     /**
306      * Enables the descriptor by loading the module class. Classes overriding this method MUST
307      * call super.enabled() before their own enabling code.
308      *
309      * @since 2.1.0
310      */
311     public void enabled()
312     {
313         loadClass(plugin, moduleClassName);
314         enabled = true;
315     }
316 
317     /**
318      * Disables the module descriptor. Classes overriding this method MUST call super.disabled() after
319      * their own disabling code.
320      */
321     public void disabled()
322     {
323         enabled = false;
324     }
325 }