1 package com.atlassian.plugin.parsers;
2
3 import com.atlassian.plugin.ModuleDescriptor;
4 import com.atlassian.plugin.ModuleDescriptorFactory;
5 import com.atlassian.plugin.Plugin;
6 import com.atlassian.plugin.PluginInformation;
7 import com.atlassian.plugin.PluginParseException;
8 import com.atlassian.plugin.Resources;
9 import com.atlassian.plugin.descriptors.UnloadableModuleDescriptor;
10 import com.atlassian.plugin.descriptors.UnloadableModuleDescriptorFactory;
11 import com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptor;
12 import com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptorFactory;
13 import com.atlassian.plugin.impl.UnloadablePluginFactory;
14 import com.atlassian.plugin.util.PluginUtils;
15
16 import org.apache.commons.lang.Validate;
17 import org.dom4j.Document;
18 import org.dom4j.DocumentException;
19 import org.dom4j.Element;
20 import org.dom4j.io.SAXReader;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import java.io.InputStream;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.Set;
30
31
32
33
34
35
36
37
38
39 public class XmlDescriptorParser implements DescriptorParser
40 {
41 private static final Logger log = LoggerFactory.getLogger(XmlDescriptorParser.class);
42
43 private final Document document;
44 private final Set<String> applicationKeys;
45
46
47
48
49
50
51
52
53 public XmlDescriptorParser(final Document source, final String... applicationKeys) throws PluginParseException
54 {
55 Validate.notNull(source, "XML descriptor source document cannot be null");
56 document = source;
57 if (applicationKeys == null)
58 {
59 this.applicationKeys = Collections.emptySet();
60 }
61 else
62 {
63 this.applicationKeys = new HashSet<String>(Arrays.asList(applicationKeys));
64 }
65 }
66
67
68
69
70
71
72
73
74 public XmlDescriptorParser(final InputStream source, final String... applicationKeys) throws PluginParseException
75 {
76 Validate.notNull(source, "XML descriptor source cannot be null");
77 document = createDocument(source);
78 if (applicationKeys == null)
79 {
80 this.applicationKeys = Collections.emptySet();
81 }
82 else
83 {
84 this.applicationKeys = new HashSet<String>(Arrays.asList(applicationKeys));
85 }
86 }
87
88 protected Document createDocument(final InputStream source) throws PluginParseException
89 {
90 final SAXReader reader = new SAXReader();
91 reader.setMergeAdjacentText(true);
92 try
93 {
94 return reader.read(source);
95 }
96 catch (final DocumentException e)
97 {
98 throw new PluginParseException("Cannot parse XML plugin descriptor", e);
99 }
100 }
101
102 protected Document getDocument()
103 {
104 return document;
105 }
106
107 public Plugin configurePlugin(final ModuleDescriptorFactory moduleDescriptorFactory, final Plugin plugin) throws PluginParseException
108 {
109 final Element pluginElement = getPluginElement();
110 plugin.setName(pluginElement.attributeValue("name"));
111 plugin.setKey(getKey());
112 plugin.setPluginsVersion(getPluginsVersion());
113 plugin.setSystemPlugin(isSystemPlugin());
114
115 if (pluginElement.attributeValue("i18n-name-key") != null)
116 {
117 plugin.setI18nNameKey(pluginElement.attributeValue("i18n-name-key"));
118 }
119
120 if (plugin.getKey().indexOf(":") > 0)
121 {
122 throw new PluginParseException("Plugin keys cannot contain ':'. Key is '" + plugin.getKey() + "'");
123 }
124
125 if ("disabled".equalsIgnoreCase(pluginElement.attributeValue("state")))
126 {
127 plugin.setEnabledByDefault(false);
128 }
129
130 plugin.setResources(Resources.fromXml(pluginElement));
131
132 for (final Iterator i = pluginElement.elementIterator(); i.hasNext();)
133 {
134 final Element element = (Element) i.next();
135
136 if ("plugin-info".equalsIgnoreCase(element.getName()))
137 {
138 plugin.setPluginInformation(createPluginInformation(element));
139 }
140 else if (!"resource".equalsIgnoreCase(element.getName()))
141 {
142 final ModuleDescriptor<?> moduleDescriptor = createModuleDescriptor(plugin, element, moduleDescriptorFactory);
143
144
145 if (moduleDescriptor == null)
146 {
147 continue;
148 }
149
150 if (plugin.getModuleDescriptor(moduleDescriptor.getKey()) != null)
151 {
152 throw new PluginParseException("Found duplicate key '" + moduleDescriptor.getKey() + "' within plugin '" + plugin.getKey() + "'");
153 }
154
155 plugin.addModuleDescriptor(moduleDescriptor);
156
157
158 if (moduleDescriptor instanceof UnloadableModuleDescriptor)
159 {
160 log.error("There were errors loading the plugin '" + plugin.getName() + "'. The plugin has been disabled.");
161 return UnloadablePluginFactory.createUnloadablePlugin(plugin);
162 }
163 }
164 }
165
166 return plugin;
167 }
168
169 private Element getPluginElement()
170 {
171 return document.getRootElement();
172 }
173
174 protected ModuleDescriptor<?> createModuleDescriptor(final Plugin plugin, final Element element, final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
175 {
176 final String name = element.getName();
177
178
179 if (!PluginUtils.doesModuleElementApplyToApplication(element, applicationKeys))
180 {
181 log.debug("Ignoring module descriptor for this application: " + element.attributeValue("key"));
182 return null;
183 }
184
185 ModuleDescriptor<?> moduleDescriptorDescriptor;
186
187
188 try
189 {
190 moduleDescriptorDescriptor = moduleDescriptorFactory.getModuleDescriptor(name);
191 }
192
193 catch (final Throwable e)
194 {
195 final UnrecognisedModuleDescriptor descriptor = UnrecognisedModuleDescriptorFactory.createUnrecognisedModuleDescriptor(plugin, element,
196 e, moduleDescriptorFactory);
197
198 log.error("There were problems loading the module '" + name + "' in plugin '" + plugin.getName() + "'. The module has been disabled.");
199 log.error(descriptor.getErrorText(), e);
200
201 return descriptor;
202 }
203
204
205 if (moduleDescriptorDescriptor == null)
206 {
207 log.info("The module '" + name + "' in plugin '" + plugin.getName() + "' is in the list of excluded module descriptors, so not enabling.");
208 return null;
209 }
210
211
212 try
213 {
214 moduleDescriptorDescriptor.init(plugin, element);
215 }
216
217 catch (final Exception e)
218 {
219 final UnloadableModuleDescriptor descriptor = UnloadableModuleDescriptorFactory.createUnloadableModuleDescriptor(plugin, element, e,
220 moduleDescriptorFactory);
221
222 log.error("There were problems loading the module '" + name + "'. The module and its plugin have been disabled.");
223 log.error(descriptor.getErrorText(), e);
224
225 return descriptor;
226 }
227
228 return moduleDescriptorDescriptor;
229 }
230
231 protected PluginInformation createPluginInformation(final Element element)
232 {
233 final PluginInformation pluginInfo = new PluginInformation();
234
235 if (element.element("description") != null)
236 {
237 pluginInfo.setDescription(element.element("description").getTextTrim());
238 if (element.element("description").attributeValue("key") != null)
239 {
240 pluginInfo.setDescriptionKey(element.element("description").attributeValue("key"));
241 }
242 }
243
244 if (element.element("version") != null)
245 {
246 pluginInfo.setVersion(element.element("version").getTextTrim());
247 }
248
249 if (element.element("vendor") != null)
250 {
251 final Element vendor = element.element("vendor");
252 pluginInfo.setVendorName(vendor.attributeValue("name"));
253 pluginInfo.setVendorUrl(vendor.attributeValue("url"));
254 }
255
256
257 for (final Iterator<Element> iterator = element.elements("param").iterator(); iterator.hasNext();)
258 {
259 final Element param = iterator.next();
260
261
262 if (param.attribute("name") != null)
263 {
264 pluginInfo.addParameter(param.attribute("name").getData().toString(), param.getText());
265 }
266 }
267
268 if (element.element("application-version") != null)
269 {
270 final Element ver = element.element("application-version");
271 if (ver.attribute("max") != null)
272 {
273 pluginInfo.setMaxVersion(Float.parseFloat(ver.attributeValue("max")));
274 }
275 if (ver.attribute("min") != null)
276 {
277 pluginInfo.setMinVersion(Float.parseFloat(ver.attributeValue("min")));
278 }
279 }
280
281 if (element.element("java-version") != null)
282 {
283 pluginInfo.setMinJavaVersion(Float.valueOf(element.element("java-version").attributeValue("min")));
284 }
285
286 return pluginInfo;
287 }
288
289 public String getKey()
290 {
291 return getPluginElement().attributeValue("key");
292 }
293
294 public int getPluginsVersion()
295 {
296 String val = getPluginElement().attributeValue("pluginsVersion");
297 if (val == null)
298 {
299 val = getPluginElement().attributeValue("plugins-version");
300 }
301 if (val != null)
302 {
303 try
304 {
305 return Integer.parseInt(val);
306 }
307 catch (final NumberFormatException e)
308 {
309 throw new RuntimeException("Could not parse pluginsVersion: " + e.getMessage(), e);
310 }
311 }
312 else
313 {
314 return 1;
315 }
316 }
317
318 public PluginInformation getPluginInformation()
319 {
320 return createPluginInformation(getDocument().getRootElement().element("plugin-info"));
321 }
322
323 public boolean isSystemPlugin()
324 {
325 return "true".equalsIgnoreCase(getPluginElement().attributeValue("system"));
326 }
327 }