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.apache.commons.logging.Log;
18 import org.apache.commons.logging.LogFactory;
19 import org.dom4j.Document;
20 import org.dom4j.DocumentException;
21 import org.dom4j.Element;
22 import org.dom4j.io.SAXReader;
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 Log log = LogFactory.getLog(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 try
92 {
93 return reader.read(source);
94 }
95 catch (final DocumentException e)
96 {
97 throw new PluginParseException("Cannot parse XML plugin descriptor", e);
98 }
99 }
100
101 protected Document getDocument()
102 {
103 return document;
104 }
105
106 public Plugin configurePlugin(final ModuleDescriptorFactory moduleDescriptorFactory, final Plugin plugin) throws PluginParseException
107 {
108
109 final Element pluginElement = getPluginElement();
110 plugin.setName(pluginElement.attributeValue("name"));
111 plugin.setKey(getKey());
112 plugin.setPluginsVersion(getPluginsVersion());
113
114 if (pluginElement.attributeValue("i18n-name-key") != null)
115 {
116 plugin.setI18nNameKey(pluginElement.attributeValue("i18n-name-key"));
117 }
118
119 if (plugin.getKey().indexOf(":") > 0)
120 {
121 throw new PluginParseException("Plugin keys cannot contain ':'. Key is '" + plugin.getKey() + "'");
122 }
123
124 if ("disabled".equalsIgnoreCase(pluginElement.attributeValue("state")))
125 {
126 plugin.setEnabledByDefault(false);
127 }
128
129 for (final Iterator i = pluginElement.elementIterator(); i.hasNext();)
130 {
131 final Element element = (Element) i.next();
132
133 if ("plugin-info".equalsIgnoreCase(element.getName()))
134 {
135 plugin.setPluginInformation(createPluginInformation(element));
136 }
137 else if (!"resource".equalsIgnoreCase(element.getName()))
138 {
139 final ModuleDescriptor<?> moduleDescriptor = createModuleDescriptor(plugin, element, moduleDescriptorFactory);
140
141
142 if (moduleDescriptor == null)
143 {
144 continue;
145 }
146
147 if (plugin.getModuleDescriptor(moduleDescriptor.getKey()) != null)
148 {
149 throw new PluginParseException("Found duplicate key '" + moduleDescriptor.getKey() + "' within plugin '" + plugin.getKey() + "'");
150 }
151
152 plugin.addModuleDescriptor(moduleDescriptor);
153
154
155 if (moduleDescriptor instanceof UnloadableModuleDescriptor)
156 {
157 log.error("There were errors loading the plugin '" + plugin.getName() + "'. The plugin has been disabled.");
158 return UnloadablePluginFactory.createUnloadablePlugin(plugin);
159 }
160 }
161 }
162
163 plugin.setResources(Resources.fromXml(pluginElement));
164
165 return plugin;
166 }
167
168 private Element getPluginElement()
169 {
170 return document.getRootElement();
171 }
172
173 protected ModuleDescriptor<?> createModuleDescriptor(final Plugin plugin, final Element element, final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
174 {
175 final String name = element.getName();
176
177
178 if (!PluginUtils.doesModuleElementApplyToApplication(element, applicationKeys))
179 {
180 log.debug("Ignoring module descriptor for this application: " + element.attributeValue("key"));
181 return null;
182 }
183
184 ModuleDescriptor<?> moduleDescriptorDescriptor;
185
186
187 try
188 {
189 moduleDescriptorDescriptor = moduleDescriptorFactory.getModuleDescriptor(name);
190 }
191
192 catch (final Throwable e)
193 {
194 final UnrecognisedModuleDescriptor descriptor = UnrecognisedModuleDescriptorFactory.createUnrecognisedModuleDescriptor(plugin, element,
195 e, moduleDescriptorFactory);
196
197 log.error("There were problems loading the module '" + name + "' in plugin '" + plugin.getName() + "'. The module has been disabled.");
198 log.error(descriptor.getErrorText(), e);
199
200 return descriptor;
201 }
202
203
204 if (moduleDescriptorDescriptor == null)
205 {
206 log.info("The module '" + name + "' in plugin '" + plugin.getName() + "' is in the list of excluded module descriptors, so not enabling.");
207 return null;
208 }
209
210
211 try
212 {
213 moduleDescriptorDescriptor.init(plugin, element);
214 }
215
216 catch (final Exception e)
217 {
218 final UnloadableModuleDescriptor descriptor = UnloadableModuleDescriptorFactory.createUnloadableModuleDescriptor(plugin, element, e,
219 moduleDescriptorFactory);
220
221 log.error("There were problems loading the module '" + name + "'. The module and its plugin have been disabled.");
222 log.error(descriptor.getErrorText(), e);
223
224 return descriptor;
225 }
226
227 return moduleDescriptorDescriptor;
228 }
229
230 protected PluginInformation createPluginInformation(final Element element)
231 {
232 final PluginInformation pluginInfo = new PluginInformation();
233
234 if (element.element("description") != null)
235 {
236 pluginInfo.setDescription(element.element("description").getTextTrim());
237 if (element.element("description").attributeValue("key") != null)
238 {
239 pluginInfo.setDescriptionKey(element.element("description").attributeValue("key"));
240 }
241 }
242
243 if (element.element("version") != null)
244 {
245 pluginInfo.setVersion(element.element("version").getTextTrim());
246 }
247
248 if (element.element("vendor") != null)
249 {
250 final Element vendor = element.element("vendor");
251 pluginInfo.setVendorName(vendor.attributeValue("name"));
252 pluginInfo.setVendorUrl(vendor.attributeValue("url"));
253 }
254
255
256 for (final Iterator<Element> iterator = element.elements("param").iterator(); iterator.hasNext();)
257 {
258 final Element param = iterator.next();
259
260
261 if (param.attribute("name") != null)
262 {
263 pluginInfo.addParameter(param.attribute("name").getData().toString(), param.getText());
264 }
265 }
266
267 if (element.element("application-version") != null)
268 {
269 final Element ver = element.element("application-version");
270 if (ver.attribute("max") != null)
271 {
272 pluginInfo.setMaxVersion(Float.parseFloat(ver.attributeValue("max")));
273 }
274 if (ver.attribute("min") != null)
275 {
276 pluginInfo.setMinVersion(Float.parseFloat(ver.attributeValue("min")));
277 }
278 }
279
280 if (element.element("java-version") != null)
281 {
282 pluginInfo.setMinJavaVersion(Float.valueOf(element.element("java-version").attributeValue("min")));
283 }
284
285 return pluginInfo;
286 }
287
288 public String getKey()
289 {
290 return getPluginElement().attributeValue("key");
291 }
292
293 public int getPluginsVersion()
294 {
295 String val = getPluginElement().attributeValue("pluginsVersion");
296 if (val == null)
297 {
298 val = getPluginElement().attributeValue("plugins-version");
299 }
300 if (val != null)
301 {
302 return Integer.parseInt(val);
303 }
304 else
305 {
306 return 1;
307 }
308 }
309
310 public PluginInformation getPluginInformation()
311 {
312 return createPluginInformation(getDocument().getRootElement().element("plugin-info"));
313 }
314
315 public boolean isSystemPlugin()
316 {
317 return "true".equalsIgnoreCase(getPluginElement().attributeValue("system"));
318 }
319 }