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