1 package com.atlassian.plugin.impl;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Date;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10 import java.util.concurrent.atomic.AtomicReference;
11
12 import org.apache.commons.lang.StringUtils;
13 import org.slf4j.Logger;
14 import org.slf4j.LoggerFactory;
15
16 import com.atlassian.plugin.ModuleDescriptor;
17 import com.atlassian.plugin.Plugin;
18 import com.atlassian.plugin.PluginException;
19 import com.atlassian.plugin.PluginInformation;
20 import com.atlassian.plugin.PluginState;
21 import com.atlassian.plugin.Resourced;
22 import com.atlassian.plugin.Resources;
23 import com.atlassian.plugin.elements.ResourceDescriptor;
24 import com.atlassian.plugin.elements.ResourceLocation;
25 import com.atlassian.plugin.util.VersionStringComparator;
26 import com.atlassian.util.concurrent.CopyOnWriteMap;
27
28 public abstract class AbstractPlugin implements Plugin, Comparable<Plugin>
29 {
30 private final Map<String, ModuleDescriptor<?>> modules = CopyOnWriteMap.<String, ModuleDescriptor<?>> builder().stableViews().newLinkedMap();
31 private String name;
32 private String i18nNameKey;
33 private String key;
34 private boolean enabledByDefault = true;
35 private PluginInformation pluginInformation = new PluginInformation();
36 private boolean system;
37 private Resourced resources = Resources.EMPTY_RESOURCES;
38 private int pluginsVersion = 1;
39 private final Date dateLoaded = new Date();
40 private final AtomicReference<PluginState> pluginState = new AtomicReference<PluginState>(PluginState.UNINSTALLED);
41
42 private final Logger log = LoggerFactory.getLogger(this.getClass());
43
44 public String getName()
45 {
46 return !StringUtils.isBlank(name) ? name : !StringUtils.isBlank(i18nNameKey) ? "" : getKey();
47 }
48
49 public void setName(final String name)
50 {
51 this.name = name;
52 }
53
54
55
56
57 protected Logger getLog()
58 {
59 return log;
60 }
61
62 public String getI18nNameKey()
63 {
64 return i18nNameKey;
65 }
66
67 public void setI18nNameKey(final String i18nNameKey)
68 {
69 this.i18nNameKey = i18nNameKey;
70 }
71
72 public String getKey()
73 {
74 return key;
75 }
76
77 public void setKey(final String aPackage)
78 {
79 key = aPackage;
80 }
81
82 public void addModuleDescriptor(final ModuleDescriptor<?> moduleDescriptor)
83 {
84 modules.put(moduleDescriptor.getKey(), moduleDescriptor);
85 }
86
87 protected void removeModuleDescriptor(final String key)
88 {
89 modules.remove(key);
90 }
91
92
93
94
95
96
97 public Collection<ModuleDescriptor<?>> getModuleDescriptors()
98 {
99 return modules.values();
100 }
101
102 public ModuleDescriptor<?> getModuleDescriptor(final String key)
103 {
104 return modules.get(key);
105 }
106
107 public <T> List<ModuleDescriptor<T>> getModuleDescriptorsByModuleClass(final Class<T> aClass)
108 {
109 final List<ModuleDescriptor<T>> result = new ArrayList<ModuleDescriptor<T>>();
110 for (final ModuleDescriptor<?> moduleDescriptor : modules.values())
111 {
112 final Class<?> moduleClass = moduleDescriptor.getModuleClass();
113 if (moduleClass != null && aClass.isAssignableFrom(moduleClass))
114 {
115 @SuppressWarnings("unchecked")
116 final ModuleDescriptor<T> typedModuleDescriptor = (ModuleDescriptor<T>) moduleDescriptor;
117 result.add(typedModuleDescriptor);
118 }
119 }
120 return result;
121 }
122
123 public PluginState getPluginState()
124 {
125 return pluginState.get();
126 }
127
128 protected void setPluginState(final PluginState state)
129 {
130 if (log.isDebugEnabled())
131 {
132 log.debug("Plugin " + getKey() + " going from " + getPluginState() + " to " + state);
133 }
134 pluginState.set(state);
135 }
136
137
138
139
140
141
142
143
144
145
146 protected boolean compareAndSetPluginState(final PluginState requiredExistingState, final PluginState desiredState)
147 {
148 if (log.isDebugEnabled())
149 {
150 log.debug("Plugin " + getKey() + " trying to go from " + getPluginState() + " to " + desiredState + " but only if in " + requiredExistingState);
151 }
152 return pluginState.compareAndSet(requiredExistingState, desiredState);
153 }
154
155 public boolean isEnabledByDefault()
156 {
157 return enabledByDefault && ((pluginInformation == null) || pluginInformation.satisfiesMinJavaVersion());
158 }
159
160 public void setEnabledByDefault(final boolean enabledByDefault)
161 {
162 this.enabledByDefault = enabledByDefault;
163 }
164
165 public int getPluginsVersion()
166 {
167 return pluginsVersion;
168 }
169
170 public void setPluginsVersion(final int pluginsVersion)
171 {
172 this.pluginsVersion = pluginsVersion;
173 }
174
175 public PluginInformation getPluginInformation()
176 {
177 return pluginInformation;
178 }
179
180 public void setPluginInformation(final PluginInformation pluginInformation)
181 {
182 this.pluginInformation = pluginInformation;
183 }
184
185 public void setResources(final Resourced resources)
186 {
187 this.resources = resources != null ? resources : Resources.EMPTY_RESOURCES;
188 }
189
190 public List<ResourceDescriptor> getResourceDescriptors()
191 {
192 return resources.getResourceDescriptors();
193 }
194
195 public List<ResourceDescriptor> getResourceDescriptors(final String type)
196 {
197 return resources.getResourceDescriptors(type);
198 }
199
200 public ResourceLocation getResourceLocation(final String type, final String name)
201 {
202 return resources.getResourceLocation(type, name);
203 }
204
205
206
207
208 @Deprecated
209 public ResourceDescriptor getResourceDescriptor(final String type, final String name)
210 {
211 return resources.getResourceDescriptor(type, name);
212 }
213
214
215
216
217 @Deprecated
218 public boolean isEnabled()
219 {
220 return getPluginState() == PluginState.ENABLED;
221 }
222
223 public final void enable()
224 {
225 final PluginState state = pluginState.get();
226 if ((state == PluginState.ENABLED) || (state == PluginState.ENABLING))
227 {
228 return;
229 }
230 if (getLog().isDebugEnabled())
231 {
232 getLog().debug("Enabling plugin '" + getKey() + "'");
233 }
234 try
235 {
236
237 final PluginState desiredState = enableInternal();
238 if ((desiredState != PluginState.ENABLED) && (desiredState != PluginState.ENABLING))
239 {
240 log.warn("Illegal state transition to " + desiredState + " for plugin '" + getKey() + "' on enable()");
241 }
242 setPluginState(desiredState);
243 }
244 catch (final PluginException ex)
245 {
246 log.warn("Unable to enable plugin '" + getKey() + "'", ex);
247 throw ex;
248 }
249 if (getLog().isDebugEnabled())
250 {
251 getLog().debug("Enabled plugin '" + getKey() + "'");
252 }
253 }
254
255
256
257
258
259
260
261
262
263
264 protected PluginState enableInternal() throws PluginException
265 {
266 return PluginState.ENABLED;
267 }
268
269 public final void disable()
270 {
271 if (pluginState.get() == PluginState.DISABLED)
272 {
273 return;
274 }
275 if (getLog().isDebugEnabled())
276 {
277 getLog().debug("Disabling plugin '" + getKey() + "'");
278 }
279 try
280 {
281 setPluginState(PluginState.DISABLING);
282 disableInternal();
283 setPluginState(PluginState.DISABLED);
284 }
285 catch (final PluginException ex)
286 {
287 setPluginState(PluginState.ENABLED);
288 log.warn("Unable to disable plugin '" + getKey() + "'", ex);
289 throw ex;
290 }
291 if (getLog().isDebugEnabled())
292 {
293 getLog().debug("Disabled plugin '" + getKey() + "'");
294 }
295 }
296
297
298
299
300
301
302
303
304 protected void disableInternal() throws PluginException
305 {}
306
307 public Set<String> getRequiredPlugins()
308 {
309 return Collections.emptySet();
310 }
311
312 public void close()
313 {
314 uninstall();
315 }
316
317 public final void install()
318 {
319 if (pluginState.get() == PluginState.INSTALLED)
320 {
321 return;
322 }
323 if (getLog().isDebugEnabled())
324 {
325 getLog().debug("Installing plugin '" + getKey() + "'");
326 }
327 try
328 {
329 installInternal();
330 setPluginState(PluginState.INSTALLED);
331 }
332 catch (final PluginException ex)
333 {
334 log.warn("Unable to install plugin '" + getKey() + "'", ex);
335 throw ex;
336 }
337 if (getLog().isDebugEnabled())
338 {
339 getLog().debug("Installed plugin '" + getKey() + "'");
340 }
341 }
342
343
344
345
346
347
348
349
350 protected void installInternal() throws PluginException
351 {}
352
353 public final void uninstall()
354 {
355 if (pluginState.get() == PluginState.UNINSTALLED)
356 {
357 return;
358 }
359 if (getLog().isDebugEnabled())
360 {
361 getLog().debug("Uninstalling plugin '" + getKey() + "'");
362 }
363 try
364 {
365 uninstallInternal();
366 setPluginState(PluginState.UNINSTALLED);
367 }
368 catch (final PluginException ex)
369 {
370 log.warn("Unable to uninstall plugin '" + getKey() + "'", ex);
371 throw ex;
372 }
373 if (getLog().isDebugEnabled())
374 {
375 getLog().debug("Uninstalled plugin '" + getKey() + "'");
376 }
377 }
378
379
380
381
382
383
384
385
386 protected void uninstallInternal() throws PluginException
387 {}
388
389
390
391
392
393 @Deprecated
394 public void setEnabled(final boolean enabled)
395 {
396 if (enabled)
397 {
398 enable();
399 }
400 else
401 {
402 disable();
403 }
404 }
405
406 public boolean isSystemPlugin()
407 {
408 return system;
409 }
410
411 public boolean containsSystemModule()
412 {
413 for (final ModuleDescriptor<?> moduleDescriptor : modules.values())
414 {
415 if (moduleDescriptor.isSystemModule())
416 {
417 return true;
418 }
419 }
420 return false;
421 }
422
423 public void setSystemPlugin(final boolean system)
424 {
425 this.system = system;
426 }
427
428 public Date getDateLoaded()
429 {
430 return dateLoaded;
431 }
432
433 public boolean isBundledPlugin()
434 {
435 return false;
436 }
437
438
439
440
441
442
443
444
445
446
447
448 public int compareTo(final Plugin otherPlugin)
449 {
450 if (otherPlugin.getKey() == null)
451 {
452 if (getKey() == null)
453 {
454
455
456 return 0;
457 }
458 return 1;
459 }
460 if (getKey() == null)
461 {
462 return -1;
463 }
464
465
466
467 if (!otherPlugin.getKey().equals(getKey()))
468 {
469 return getKey().compareTo(otherPlugin.getKey());
470 }
471
472 final String thisVersion = cleanVersionString((getPluginInformation() != null ? getPluginInformation().getVersion() : null));
473 final String otherVersion = cleanVersionString((otherPlugin.getPluginInformation() != null ? otherPlugin.getPluginInformation().getVersion() : null));
474
475
476
477 if (!VersionStringComparator.isValidVersionString(thisVersion))
478 {
479 if (!VersionStringComparator.isValidVersionString(otherVersion))
480 {
481
482 return 0;
483 }
484 return -1;
485 }
486 if (!VersionStringComparator.isValidVersionString(otherVersion))
487 {
488 return 1;
489 }
490
491 return new VersionStringComparator().compare(thisVersion, otherVersion);
492 }
493
494 private String cleanVersionString(final String version)
495 {
496 if ((version == null) || version.trim().equals(""))
497 {
498 return "0";
499 }
500 return version.replaceAll(" ", "");
501 }
502
503 @Override
504 public String toString()
505 {
506 final PluginInformation info = getPluginInformation();
507 return getKey() + ":" + (info == null ? "?" : info.getVersion());
508 }
509 }