View Javadoc

1   package com.atlassian.plugin.impl;
2   
3   import static com.atlassian.plugin.util.concurrent.CopyOnWriteMap.newLinkedMap;
4   
5   import com.atlassian.plugin.*;
6   import com.atlassian.plugin.elements.ResourceDescriptor;
7   import com.atlassian.plugin.elements.ResourceLocation;
8   import com.atlassian.plugin.util.VersionStringComparator;
9   
10  import java.util.*;
11  
12  import org.apache.commons.logging.Log;
13  import org.apache.commons.logging.LogFactory;
14  
15  public abstract class AbstractPlugin implements Plugin, Comparable<Plugin>
16  {
17      private final Map<String, ModuleDescriptor<?>> modules = newLinkedMap();
18      private String name;
19      private String i18nNameKey;
20      private String key;
21      private boolean enabledByDefault = true;
22      private PluginInformation pluginInformation = new PluginInformation();
23      private boolean system;
24      private Resourced resources = Resources.EMPTY_RESOURCES;
25      private int pluginsVersion = 1;
26      private final Date dateLoaded = new Date();
27      private volatile PluginState pluginState = PluginState.UNINSTALLED;
28  
29      private final Log log = LogFactory.getLog(this.getClass());
30  
31      public String getName()
32      {
33          return name;
34      }
35  
36      public void setName(final String name)
37      {
38          this.name = name;
39      }
40  
41      /**
42       * @return the logger used internally
43       */
44      protected Log getLog()
45      {
46          return log;
47      }
48  
49      public String getI18nNameKey()
50      {
51          return i18nNameKey;
52      }
53  
54      public void setI18nNameKey(final String i18nNameKey)
55      {
56          this.i18nNameKey = i18nNameKey;
57      }
58  
59      public String getKey()
60      {
61          return key;
62      }
63  
64      public void setKey(final String aPackage)
65      {
66          key = aPackage;
67      }
68  
69      public void addModuleDescriptor(final ModuleDescriptor<?> moduleDescriptor)
70      {
71          modules.put(moduleDescriptor.getKey(), moduleDescriptor);
72      }
73  
74      protected void removeModuleDescriptor(final String key)
75      {
76          modules.remove(key);
77      }
78  
79      /**
80       * Returns a copy of the module descriptors for this plugin
81       * @return A copy of the internal list
82       */
83      public Collection<ModuleDescriptor<?>> getModuleDescriptors()
84      {
85          return new ArrayList<ModuleDescriptor<?>>(modules.values());
86      }
87  
88      public ModuleDescriptor<?> getModuleDescriptor(final String key)
89      {
90          return modules.get(key);
91      }
92  
93      public <T> List<ModuleDescriptor<T>> getModuleDescriptorsByModuleClass(final Class<T> aClass)
94      {
95          final List<ModuleDescriptor<T>> result = new ArrayList<ModuleDescriptor<T>>();
96          for (final ModuleDescriptor<?> moduleDescriptor : modules.values())
97          {
98              final Class<?> moduleClass = moduleDescriptor.getModuleClass();
99              if (aClass.isAssignableFrom(moduleClass))
100             {
101                 @SuppressWarnings("unchecked")
102                 final ModuleDescriptor<T> typedModuleDescriptor = (ModuleDescriptor<T>) moduleDescriptor;
103                 result.add(typedModuleDescriptor);
104             }
105         }
106         return result;
107     }
108 
109     public PluginState getPluginState()
110     {
111         return pluginState;
112     }
113 
114     protected void setPluginState(PluginState state)
115     {
116         pluginState = state;
117     }
118 
119     public boolean isEnabledByDefault()
120     {
121         return enabledByDefault && ((pluginInformation == null) || pluginInformation.satisfiesMinJavaVersion());
122     }
123 
124     public void setEnabledByDefault(final boolean enabledByDefault)
125     {
126         this.enabledByDefault = enabledByDefault;
127     }
128 
129     public int getPluginsVersion()
130     {
131         return pluginsVersion;
132     }
133 
134     public void setPluginsVersion(final int pluginsVersion)
135     {
136         this.pluginsVersion = pluginsVersion;
137     }
138 
139     public PluginInformation getPluginInformation()
140     {
141         return pluginInformation;
142     }
143 
144     public void setPluginInformation(final PluginInformation pluginInformation)
145     {
146         this.pluginInformation = pluginInformation;
147     }
148 
149     public void setResources(final Resourced resources)
150     {
151         this.resources = resources != null ? resources : Resources.EMPTY_RESOURCES;
152     }
153 
154     public List<ResourceDescriptor> getResourceDescriptors()
155     {
156         return resources.getResourceDescriptors();
157     }
158 
159     public List<ResourceDescriptor> getResourceDescriptors(final String type)
160     {
161         return resources.getResourceDescriptors(type);
162     }
163 
164     public ResourceLocation getResourceLocation(final String type, final String name)
165     {
166         return resources.getResourceLocation(type, name);
167     }
168 
169     /**
170      * @deprecated
171      */
172     @Deprecated
173     public ResourceDescriptor getResourceDescriptor(final String type, final String name)
174     {
175         return resources.getResourceDescriptor(type, name);
176     }
177 
178     /**
179      * @return true if the plugin has been enabled
180      */
181     @Deprecated
182     public boolean isEnabled()
183     {
184         return getPluginState() == PluginState.ENABLED;
185     }
186 
187     public final void enable()
188     {
189         if (pluginState == PluginState.ENABLED || pluginState == PluginState.ENABLING)
190         {
191             return;
192         }
193         if (getLog().isDebugEnabled())
194         {
195             getLog().debug("Enabling plugin '" + getKey() + "'");
196         }
197         try
198         {
199             pluginState = enableInternal();
200         }
201         catch (PluginException ex)
202         {
203             log.warn("Unable to enable plugin '" + getKey() + "'", ex);
204             throw ex;
205         }
206         if (getLog().isDebugEnabled())
207         {
208             getLog().debug("Enabled plugin '" + getKey() + "'");
209         }
210     }
211 
212     /**
213      * Perform any internal enabling logic.  Subclasses should only throw {@link PluginException}.
214      *
215      * @throws PluginException If the plugin could not be enabled
216      * @since 2.2.0
217      * @return Either {@link PluginState#ENABLED} or {@link PluginState#ENABLING}
218      */
219     protected PluginState enableInternal() throws PluginException
220     {
221         return PluginState.ENABLED;
222     }
223 
224     public final void disable()
225     {
226         if (pluginState == PluginState.DISABLED)
227         {
228             return;
229         }
230         if (getLog().isDebugEnabled())
231         {
232             getLog().debug("Disabling plugin '" + getKey() + "'");
233         }
234         try
235         {
236             disableInternal();
237             pluginState = PluginState.DISABLED;
238         }
239         catch (PluginException ex)
240         {
241             log.warn("Unable to disable plugin '" + getKey() + "'", ex);
242             throw ex;
243         }
244         if (getLog().isDebugEnabled())
245         {
246             getLog().debug("Disabled plugin '" + getKey() + "'");
247         }
248     }
249 
250     /**
251      * Perform any internal disabling logic.  Subclasses should only throw {@link PluginException}.
252      *
253      * @throws PluginException If the plugin could not be disabled
254      * @since 2.2.0
255      */
256     protected void disableInternal() throws PluginException
257     {
258     }
259 
260     public Set<String> getRequiredPlugins()
261     {
262         return Collections.emptySet();
263     }
264 
265     public void close()
266     {
267         uninstall();
268     }
269 
270     public final void install()
271     {
272         if (pluginState == PluginState.INSTALLED)
273         {
274             return;
275         }
276         if (getLog().isDebugEnabled())
277         {
278             getLog().debug("Installing plugin '" + getKey() + "'");
279         }
280         try
281         {
282             installInternal();
283             pluginState = PluginState.INSTALLED;
284         }
285         catch (PluginException ex)
286         {
287             log.warn("Unable to install plugin '" + getKey() + "'", ex);
288             throw ex;
289         }
290         if (getLog().isDebugEnabled())
291         {
292             getLog().debug("Installed plugin '" + getKey() + "'");
293         }
294     }
295 
296     /**
297      * Perform any internal installation logic.  Subclasses should only throw {@link PluginException}.
298      *
299      * @throws PluginException If the plugin could not be installed
300      * @since 2.2.0
301      */
302     protected void installInternal() throws PluginException
303     {
304     }
305 
306     public final void uninstall()
307     {
308         if (pluginState == PluginState.UNINSTALLED)
309         {
310             return;
311         }
312         if (getLog().isDebugEnabled())
313         {
314             getLog().debug("Uninstalling plugin '" + getKey() + "'");
315         }
316         try
317         {
318             uninstallInternal();
319             pluginState = PluginState.UNINSTALLED;
320         }
321         catch (PluginException ex)
322         {
323             log.warn("Unable to uninstall plugin '" + getKey() + "'", ex);
324             throw ex;
325         }
326         if (getLog().isDebugEnabled())
327         {
328             getLog().debug("Uninstalled plugin '" + getKey() + "'");
329         }
330     }
331 
332     /**
333      * Perform any internal uninstallation logic.  Subclasses should only throw {@link PluginException}.
334      *
335      * @throws PluginException If the plugin could not be uninstalled
336      * @since 2.2.0
337      */
338     protected void uninstallInternal() throws PluginException
339     {
340     }
341 
342     /**
343      * Setter for the enabled state of a plugin. If this is set to false then the plugin will not execute.
344      */
345     @Deprecated
346     public void setEnabled(final boolean enabled)
347     {
348         if (enabled)
349         {
350             enable();
351         }
352         else
353         {
354             disable();
355         }
356     }
357 
358     public boolean isSystemPlugin()
359     {
360         return system;
361     }
362 
363     public boolean containsSystemModule()
364     {
365         for (final ModuleDescriptor<?> moduleDescriptor : modules.values())
366         {
367             if (moduleDescriptor.isSystemModule())
368             {
369                 return true;
370             }
371         }
372         return false;
373     }
374 
375     public void setSystemPlugin(final boolean system)
376     {
377         this.system = system;
378     }
379 
380     public Date getDateLoaded()
381     {
382         return dateLoaded;
383     }
384 
385     public boolean isBundledPlugin()
386     {
387         return false;
388     }
389 
390     /**
391      * Compares this Plugin to another Plugin for order.
392      * The primary sort field is the key, and the secondary field is the version number.
393      *
394      * @param otherPlugin The plugin to be compared.
395      * @return  a negative integer, zero, or a positive integer as this Plugin is less than, equal to, or greater than the specified Plugin.
396      * @see VersionStringComparator
397      * @see Comparable#compareTo
398      */
399     public int compareTo(final Plugin otherPlugin)
400     {
401         if (otherPlugin.getKey() == null)
402         {
403             if (getKey() == null)
404             {
405                 // both null keys - not going to bother checking the version, who cares?
406                 return 0;
407             }
408             return 1;
409         }
410         if (getKey() == null)
411         {
412             return -1;
413         }
414 
415         // If the compared plugin doesn't have the same key, the current object is greater
416         if (!otherPlugin.getKey().equals(getKey()))
417         {
418             return getKey().compareTo(otherPlugin.getKey());
419         }
420 
421         final String thisVersion = cleanVersionString((getPluginInformation() != null ? getPluginInformation().getVersion() : null));
422         final String otherVersion = cleanVersionString((otherPlugin.getPluginInformation() != null ? otherPlugin.getPluginInformation().getVersion() : null));
423 
424         // Valid versions should come after invalid versions because when we find multiple instances of a plugin, we choose the "latest".
425         if (!VersionStringComparator.isValidVersionString(thisVersion))
426         {
427             if (!VersionStringComparator.isValidVersionString(otherVersion))
428             {
429                 // both invalid
430                 return 0;
431             }
432             return -1;
433         }
434         if (!VersionStringComparator.isValidVersionString(otherVersion))
435         {
436             return 1;
437         }
438 
439         return new VersionStringComparator().compare(thisVersion, otherVersion);
440     }
441 
442     private String cleanVersionString(final String version)
443     {
444         if ((version == null) || version.trim().equals(""))
445         {
446             return "0";
447         }
448         return version.replaceAll(" ", "");
449     }
450 
451     @Override
452     public String toString()
453     {
454         final PluginInformation info = getPluginInformation();
455         return getKey() + ":" + (info == null ? "?" : info.getVersion());
456     }
457 }