1 package com.atlassian.plugin.parsers;
2
3 import com.atlassian.fugue.Option;
4 import com.atlassian.plugin.Application;
5 import com.atlassian.plugin.InstallationMode;
6 import com.atlassian.plugin.ModuleDescriptor;
7 import com.atlassian.plugin.ModuleDescriptorFactory;
8 import com.atlassian.plugin.Plugin;
9 import com.atlassian.plugin.PluginInformation;
10 import com.atlassian.plugin.PluginParseException;
11 import com.atlassian.plugin.PluginPermission;
12 import com.atlassian.plugin.descriptors.UnloadableModuleDescriptor;
13 import com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptor;
14 import com.atlassian.plugin.impl.UnloadablePluginFactory;
15 import com.google.common.base.Function;
16 import com.google.common.collect.ImmutableSet;
17 import org.apache.commons.lang.StringUtils;
18 import org.dom4j.Document;
19 import org.dom4j.DocumentException;
20 import org.dom4j.Element;
21 import org.dom4j.io.SAXReader;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 import java.io.InputStream;
26 import java.util.Map;
27 import java.util.Set;
28
29 import static com.atlassian.plugin.descriptors.UnloadableModuleDescriptorFactory.createUnloadableModuleDescriptor;
30 import static com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptorFactory.createUnrecognisedModuleDescriptor;
31 import static com.google.common.base.Preconditions.checkNotNull;
32 import static com.google.common.collect.ImmutableList.copyOf;
33 import static com.google.common.collect.Iterables.transform;
34
35
36
37
38
39
40
41
42
43 public class XmlDescriptorParser implements DescriptorParser
44 {
45 private static final Logger log = LoggerFactory.getLogger(XmlDescriptorParser.class);
46
47 private final PluginDescriptorReader descriptorReader;
48
49
50
51
52
53
54
55
56
57 public XmlDescriptorParser(final Document source, final Set<Application> applications) throws PluginParseException
58 {
59 this.descriptorReader = new PluginDescriptorReader(checkNotNull(source, "XML descriptor source document cannot be null"), checkNotNull(applications));
60 }
61
62
63
64
65
66
67
68
69
70 public XmlDescriptorParser(final InputStream source, final Set<Application> applications) throws PluginParseException
71 {
72 this(createDocument(checkNotNull(source, "XML descriptor source cannot be null")), applications);
73 }
74
75 protected static Document createDocument(final InputStream source) throws PluginParseException
76 {
77 final SAXReader reader = new SAXReader();
78 reader.setMergeAdjacentText(true);
79 try
80 {
81 return reader.read(source);
82 }
83 catch (final DocumentException e)
84 {
85 throw new PluginParseException("Cannot parse XML plugin descriptor", e);
86 }
87 }
88
89 protected Document getDocument()
90 {
91 return descriptorReader.getDescriptor();
92 }
93
94 public Plugin configurePlugin(final ModuleDescriptorFactory moduleDescriptorFactory, final Plugin plugin) throws PluginParseException
95 {
96 plugin.setName(descriptorReader.getPluginName());
97 plugin.setKey(getKey());
98 plugin.setPluginsVersion(getPluginsVersion());
99 plugin.setSystemPlugin(isSystemPlugin());
100 plugin.setI18nNameKey(descriptorReader.getI18nPluginNameKey().getOrElse(plugin.getI18nNameKey()));
101
102 if (plugin.getKey().indexOf(":") > 0)
103 {
104 throw new PluginParseException("Plugin keys cannot contain ':'. Key is '" + plugin.getKey() + "'");
105 }
106
107 plugin.setEnabledByDefault(descriptorReader.isEnabledByDefault());
108 plugin.setResources(descriptorReader.getResources());
109 plugin.setPluginInformation(createPluginInformation());
110
111 for (Element module : descriptorReader.getModules(plugin.getInstallationMode()))
112 {
113 final ModuleDescriptor<?> moduleDescriptor = createModuleDescriptor(plugin, module, moduleDescriptorFactory);
114
115
116 if (moduleDescriptor == null)
117 {
118 continue;
119 }
120
121 if (plugin.getModuleDescriptor(moduleDescriptor.getKey()) != null)
122 {
123 throw new PluginParseException("Found duplicate key '" + moduleDescriptor.getKey() + "' within plugin '" + plugin.getKey() + "'");
124 }
125
126 plugin.addModuleDescriptor(moduleDescriptor);
127
128
129 if (moduleDescriptor instanceof UnloadableModuleDescriptor)
130 {
131 log.error("There were errors loading the plugin '" + plugin.getName() + "'. The plugin has been disabled.");
132 return UnloadablePluginFactory.createUnloadablePlugin(plugin);
133 }
134 }
135 return plugin;
136 }
137
138 protected ModuleDescriptor<?> createModuleDescriptor(final Plugin plugin, final Element element, final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
139 {
140 final String name = element.getName();
141
142 final ModuleDescriptor<?> moduleDescriptorDescriptor;
143
144
145 try
146 {
147 moduleDescriptorDescriptor = moduleDescriptorFactory.getModuleDescriptor(name);
148 }
149
150 catch (final Throwable e)
151 {
152 final UnrecognisedModuleDescriptor descriptor = createUnrecognisedModuleDescriptor(plugin, element, e, moduleDescriptorFactory);
153
154 log.error("There were problems loading the module '{}' in plugin '{}'. The module has been disabled.", name, plugin.getName());
155 log.error(descriptor.getErrorText(), e);
156
157 return descriptor;
158 }
159
160
161 if (moduleDescriptorDescriptor == null)
162 {
163 log.info("The module '{}' in plugin '{}' is in the list of excluded module descriptors, so not enabling.", name, plugin.getName());
164 return null;
165 }
166
167
168 try
169 {
170 moduleDescriptorDescriptor.init(plugin, element);
171 }
172
173 catch (final Exception e)
174 {
175 final UnloadableModuleDescriptor descriptor = createUnloadableModuleDescriptor(plugin, element, e, moduleDescriptorFactory);
176
177 log.error("There were problems loading the module '{}'. The module and its plugin have been disabled.", name);
178 log.error(descriptor.getErrorText(), e);
179
180 return descriptor;
181 }
182
183 return moduleDescriptorDescriptor;
184 }
185
186 protected PluginInformation createPluginInformation()
187 {
188 final PluginInformationReader pluginInformationReader = descriptorReader.getPluginInformationReader();
189
190 final PluginInformation pluginInfo = new PluginInformation();
191 pluginInfo.setDescription(pluginInformationReader.getDescription().getOrElse(pluginInfo.getDescription()));
192 pluginInfo.setDescriptionKey(pluginInformationReader.getDescriptionKey().getOrElse(pluginInfo.getDescriptionKey()));
193 pluginInfo.setVersion(pluginInformationReader.getVersion().getOrElse(pluginInfo.getVersion()));
194 pluginInfo.setVendorName(pluginInformationReader.getVendorName().getOrElse(pluginInfo.getVendorName()));
195 pluginInfo.setVendorUrl(pluginInformationReader.getVendorUrl().getOrElse(pluginInfo.getVendorUrl()));
196 for (Map.Entry<String, String> param : pluginInformationReader.getParameters().entrySet())
197 {
198 pluginInfo.addParameter(param.getKey(), param.getValue());
199 }
200 pluginInfo.setMinVersion(pluginInformationReader.getMinVersion().getOrElse(pluginInfo.getMinVersion()));
201 pluginInfo.setMaxVersion(pluginInformationReader.getMaxVersion().getOrElse(pluginInfo.getMaxVersion()));
202 pluginInfo.setMinJavaVersion(pluginInformationReader.getMinJavaVersion().getOrElse(pluginInfo.getMinJavaVersion()));
203
204 final Map<String, Option<String>> readPermissions = pluginInformationReader.getPermissions();
205 if (pluginInformationReader.hasAllPermissions())
206 {
207 pluginInfo.setPermissions(ImmutableSet.of(PluginPermission.ALL));
208 }
209 else
210 {
211 final ImmutableSet.Builder<PluginPermission> permissions = ImmutableSet.builder();
212 for (Map.Entry<String, Option<String>> permission : readPermissions.entrySet())
213 {
214 final String permissionKey = permission.getKey();
215 final Option<String> readInstallationMode = permission.getValue();
216 final Option<InstallationMode> installationMode = InstallationMode.of(readInstallationMode.getOrNull());
217 if (StringUtils.isNotBlank(readInstallationMode.getOrNull()) && !installationMode.isDefined())
218 {
219 log.warn("The parsed installation mode '{}' for permission '{}' didn't match any of the valid values: {}",
220 new Object[]{readInstallationMode, permission.getKey(),
221 transform(copyOf(InstallationMode.values()), new Function<InstallationMode, String>()
222 {
223 @Override
224 public String apply(InstallationMode im)
225 {
226 return im.getKey();
227 }
228 })});
229 }
230
231 permissions.add(new PluginPermission(permissionKey, installationMode));
232 }
233 pluginInfo.setPermissions(permissions.build());
234 }
235
236 return pluginInfo;
237 }
238
239 public String getKey()
240 {
241 return descriptorReader.getPluginKey();
242 }
243
244 public int getPluginsVersion()
245 {
246 return descriptorReader.getPluginsVersion();
247 }
248
249 public PluginInformation getPluginInformation()
250 {
251 return createPluginInformation();
252 }
253
254 public boolean isSystemPlugin()
255 {
256 return descriptorReader.isSystemPlugin();
257 }
258 }