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