View Javadoc

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   * Provides access to the descriptor information retrieved from an XML InputStream.
28   * <p/>
29   * Uses the dom4j {@link SAXReader} to parse the XML stream into a document
30   * when the parser is constructed.
31   *
32   * @see XmlDescriptorParserFactory
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       * @throws PluginParseException if there is a problem reading the descriptor from the XML {@link InputStream}.
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       * Constructs a parser with an already-constructed document
52       * @throws PluginParseException if there is a problem reading the descriptor from the XML {@link InputStream}.
53       * @since 2.2.0
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                 // If we're not loading the module descriptor, null is returned, so we skip it
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                 // If we have any unloadable modules, also create an unloadable plugin, which will make it clear that there was a problem
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         // Try to retrieve the module descriptor
153         try
154         {
155             moduleDescriptorDescriptor = moduleDescriptorFactory.getModuleDescriptor(name);
156         }
157         // When there's a problem loading a module, return an UnrecognisedModuleDescriptor with error
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         // When the module descriptor has been excluded, null is returned (PLUG-5)
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         // Once we have the module descriptor, create it using the given information
177         try
178         {
179             moduleDescriptorDescriptor.init(plugin, element);
180         }
181         // If it fails, return a dummy module that contains the error
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         // initialize any parameters on the plugin xml definition
222         for (final Iterator iterator = element.elements("param").iterator(); iterator.hasNext();)
223         {
224             final Element param = (Element) iterator.next();
225 
226             // Retrieve the parameter info => name & text
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 }