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