Coverage Report - net.sourceforge.pebble.plugins.PluginLocator
 
Classes in this File Line Coverage Branch Coverage Complexity
PluginLocator
63%
52/82
62%
15/24
4.714
PluginLocator$1
0%
0/2
N/A
4.714
PluginLocator$2
0%
0/10
0%
0/10
4.714
 
 1  
 /*
 2  
  * Copyright (c) 2003-2011, Simon Brown
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions are met:
 7  
  *
 8  
  *   - Redistributions of source code must retain the above copyright
 9  
  *     notice, this list of conditions and the following disclaimer.
 10  
  *
 11  
  *   - Redistributions in binary form must reproduce the above copyright
 12  
  *     notice, this list of conditions and the following disclaimer in
 13  
  *     the documentation and/or other materials provided with the
 14  
  *     distribution.
 15  
  *
 16  
  *   - Neither the name of Pebble nor the names of its contributors may
 17  
  *     be used to endorse or promote products derived from this software
 18  
  *     without specific prior written permission.
 19  
  *
 20  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 21  
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22  
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23  
  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 24  
  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 25  
  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 26  
  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 27  
  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 28  
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 29  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 30  
  * POSSIBILITY OF SUCH DAMAGE.
 31  
  */
 32  
 package net.sourceforge.pebble.plugins;
 33  
 
 34  
 import net.sourceforge.pebble.domain.Blog;
 35  
 import org.apache.commons.logging.Log;
 36  
 import org.apache.commons.logging.LogFactory;
 37  
 import org.jdom.Document;
 38  
 import org.jdom.Element;
 39  
 import org.jdom.JDOMException;
 40  
 import org.jdom.input.SAXBuilder;
 41  
 
 42  
 import java.io.IOException;
 43  
 import java.net.URL;
 44  
 import java.util.*;
 45  
 
 46  
 /**
 47  
  * Locates plugins
 48  
  *
 49  
  * @author James Roper
 50  
  */
 51  0
 public class PluginLocator {
 52  4
   private static final Log log = LogFactory.getLog(PluginLocator.class);
 53  
   private static final Map<String, PluginConfigType> PLUGIN_CONFIG_TYPES;
 54  
   static {
 55  4
     PLUGIN_CONFIG_TYPES = new HashMap<String, PluginConfigType>();
 56  4
     PLUGIN_CONFIG_TYPES.put("string", PlainTextPluginConfigType.INSTANCE);
 57  4
     PLUGIN_CONFIG_TYPES.put("textarea", TextAreaPluginConfigType.INSTANCE);
 58  4
     PLUGIN_CONFIG_TYPES.put("checkbox", CheckboxPluginConfigType.INSTANCE);
 59  4
     PLUGIN_CONFIG_TYPES.put("password", PasswordPluginConfigType.INSTANCE);
 60  4
   }
 61  
 
 62  
   private static AvailablePlugins availablePlugins;
 63  
 
 64  
   /**
 65  
    * Locates all the plugins installed in Pebble
 66  
    *
 67  
    * @return A map of plugin types to lists of plugin class names
 68  
    */
 69  
   public static synchronized AvailablePlugins locateAvailablePlugins() {
 70  0
     if (availablePlugins == null) {
 71  0
       Map<String, List<Plugin>> plugins = new HashMap<String, List<Plugin>>();
 72  
       try {
 73  0
         Enumeration<URL> resources = PluginLocator.class.getClassLoader().getResources("pebble-plugins.xml");
 74  0
         while (resources.hasMoreElements()) {
 75  0
           installPlugins(plugins, resources.nextElement());
 76  
         }
 77  
       }
 78  0
       catch (IOException ioe) {
 79  0
         log.error("Error occured while scanning context classloader for plugins", ioe);
 80  0
       }
 81  
       // Sort the plugins
 82  0
       for (List<Plugin> pluginsList : plugins.values())
 83  
       {
 84  0
         Collections.sort(pluginsList, new Comparator<Plugin>() {
 85  
           public int compare(Plugin o1, Plugin o2) {
 86  0
             return o1.getWeight() - o2.getWeight();
 87  
           }
 88  
         });
 89  
       }
 90  0
       availablePlugins = new AvailablePlugins(plugins);
 91  
     }
 92  0
     return availablePlugins;
 93  
   }
 94  
 
 95  
   public static AvailablePlugins getAvailablePluginsSortedForBlog(Blog blog) {
 96  0
     Map<String, List<Plugin>> plugins = locateAvailablePlugins().copyMap();
 97  
     // Sort the decorators
 98  0
     plugins.put(AvailablePlugins.CONTENT_DECORATOR, sortPlugins(plugins.get(AvailablePlugins.CONTENT_DECORATOR),
 99  
             blog.getContentDecorators()));
 100  0
     plugins.put(AvailablePlugins.PAGE_DECORATOR, sortPlugins(plugins.get(AvailablePlugins.PAGE_DECORATOR),
 101  
             blog.getPageDecoratorNames()));
 102  0
     plugins.put(AvailablePlugins.OPEN_ID_COMMENT_AUTHOR_PROVIDER, sortPlugins(plugins.get(AvailablePlugins.OPEN_ID_COMMENT_AUTHOR_PROVIDER),
 103  
             blog.getOpenIdCommentAuthorProviderNames()));
 104  0
     return new AvailablePlugins(plugins);
 105  
   }
 106  
 
 107  
   private static List<Plugin> sortPlugins(List<Plugin> plugins, final List<String> installedPlugins) {
 108  0
     if (plugins == null)
 109  
     {
 110  0
       return null;
 111  
     }
 112  0
     Collections.sort(plugins, new Comparator<Plugin>() {
 113  
       public int compare(Plugin plugin1, Plugin plugin2) {
 114  
         // This comparator ensures installed plugins are at the top, in the order specified by the user,
 115  
         // and that not installed plugins are sorted by their weight.
 116  0
         int index1 = installedPlugins.indexOf(plugin1.getPluginClass());
 117  0
         int index2 = installedPlugins.indexOf(plugin2.getPluginClass());
 118  0
         if (index1 >= 0 && index2 >= 0)
 119  
         {
 120  0
           return index1 - index2;
 121  
         }
 122  0
         if (index1 < 0 && index2 < 0)
 123  
         {
 124  0
           return plugin1.getWeight() - plugin2.getWeight();
 125  
         }
 126  0
         if (index1 >= 0)
 127  
         {
 128  0
           return -1;
 129  
         }
 130  0
         return 1;
 131  
       }
 132  
     });
 133  0
     return plugins;
 134  
   }
 135  
 
 136  
   /**
 137  
    * Install the plugins from the given resource
 138  
    * @param plugins The plugins to install
 139  
    * @param resource The resource to install them from
 140  
    */
 141  
   static void installPlugins(Map<String, List<Plugin>> plugins, URL resource) {
 142  36
     SAXBuilder saxBuilder = new SAXBuilder();
 143  
     try {
 144  36
       Document document = saxBuilder.build(resource.openStream());
 145  36
       Element root = document.getRootElement();
 146  36
       for (Element element : (Iterable<Element>) root.getChildren())
 147  
       {
 148  144
         String type = element.getName();
 149  144
         String name = element.getAttributeValue("name");
 150  144
         Element descriptionElement = element.getChild("description");
 151  144
         String description = null;
 152  144
         if (descriptionElement != null)
 153  
         {
 154  36
           description = descriptionElement.getText();
 155  
         }
 156  144
         String className = element.getAttributeValue("class");
 157  144
         String weightStr = element.getAttributeValue("weight");
 158  144
         int weight = 100;
 159  144
         if (weightStr != null)
 160  
         {
 161  
           try {
 162  36
             weight = Integer.parseInt(weightStr);
 163  0
           } catch (NumberFormatException e) {
 164  0
             log.error("Invalid weight for plugin " + name + ": " + weightStr);
 165  36
           }
 166  
         }
 167  
         // Try and load the class - for validation
 168  
         try {
 169  144
           Thread.currentThread().getContextClassLoader().loadClass(className);
 170  
           // Successful, add it
 171  144
           log.debug("Installing plugin '" + name + "' of type " + type + " with class " + className);
 172  144
           List<Plugin> list = plugins.get(type);
 173  144
           if (list == null)
 174  
           {
 175  108
             list = new ArrayList<Plugin>();
 176  108
             plugins.put(type, list);
 177  
           }
 178  144
           Collection<PluginConfig> pluginConfig = parsePluginConfig(element);
 179  144
           Plugin plugin = new Plugin(name, description, className, weight, pluginConfig);
 180  144
           if (!list.contains(plugin)) {
 181  144
             list.add(plugin);
 182  
           }
 183  0
         } catch (ClassNotFoundException e) {
 184  0
           log.error("Plugin class " + className + " of type " + type + " from descriptor " + resource +
 185  
                   " could not be found.");
 186  144
         }
 187  144
       }
 188  0
     } catch (JDOMException e) {
 189  0
       log.error("Error parsing plugin descriptor at " + resource, e);
 190  0
     } catch (IOException e) {
 191  0
       log.error("Error reading plugin descriptor at " + resource, e);
 192  36
     }
 193  36
   }
 194  
 
 195  
   private static Collection<PluginConfig> parsePluginConfig(Element element)
 196  
   {
 197  144
     Collection<PluginConfig> results = new ArrayList<PluginConfig>();
 198  144
     for (Element configElement : (Iterable<Element>) element.getChildren("config"))
 199  
     {
 200  216
       String key = configElement.getAttributeValue("key");
 201  216
       String name = configElement.getAttributeValue("name");
 202  216
       boolean required = Boolean.parseBoolean(configElement.getAttributeValue("required"));
 203  216
       String type = configElement.getAttributeValue("type");
 204  
       PluginConfigType pluginConfigType;
 205  216
       if (type == null)
 206  
       {
 207  108
         pluginConfigType = PlainTextPluginConfigType.INSTANCE;
 208  
       }
 209  
       else
 210  
       {
 211  108
         pluginConfigType = PLUGIN_CONFIG_TYPES.get(type);
 212  
       }
 213  
       // todo custom class instantiation
 214  216
       Properties properties = new Properties();
 215  216
       for (Element property : (Iterable<Element>) configElement.getChildren())
 216  
       {
 217  36
         properties.setProperty(property.getName(), property.getText());
 218  
       }
 219  216
       results.add(new PluginConfig(key, name, pluginConfigType, required, properties));
 220  216
     }
 221  144
     return results;
 222  
   }
 223  
 }