001 // GraphLab Project: http://graphlab.sharif.edu 002 // Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology 003 // Distributed under the terms of the GNU General Public License (GPL): http://www.gnu.org/licenses/ 004 005 /* 006 * Pluger.java 007 * 008 * Created on March 22, 2005, 3:17 PM 009 */ 010 package graphlab.platform.plugin; 011 012 import graphlab.platform.core.BlackBoard; 013 014 import java.io.File; 015 import java.net.MalformedURLException; 016 import java.net.URL; 017 import java.net.URLClassLoader; 018 import java.util.ArrayList; 019 import java.util.HashMap; 020 import java.util.Iterator; 021 import java.util.Map.Entry; 022 import java.util.StringTokenizer; 023 import java.util.jar.JarFile; 024 025 026 /** 027 * GraphLab plugging functionality is provided here. 028 * <p/> 029 * Class Plugger, is the heart of plugin structure. In order to gather all the plugins, you need to gather all the possible libraries(.jar) files to the project. The method plug() does this using Java Reflection API and Java Class Loader. 030 * <p/> 031 * After this, you need to read the manifest files. This is done via Java JarFile class. other properties of the plugin like its version , the dependencies and the answer to the question that this plugin should be loaded to GraphLab should be answered here. Method init(File) gets a jar file and checks all the mentioned actions using helper methods verify(), dfs(), load() and remove(). 032 * <p/> 033 * verify() reads a HashMap called depends. This Map is filled in the init() method using the property reader of JarFile class. It reads all the dependencies that are mentioned in the jar file. These are only related to version dependencies. 034 * <p/> 035 * dfs() tries a DFS algorithm to topologically sort the dependencies tree and then use the sorted trees to give priority to plugin loads. 036 * 037 * @author Reza Mohammadi 038 */ 039 public class Plugger { 040 041 public final String prefix = "graphlab.plugins."; 042 public final String postfix = ".Init"; 043 public final String handler_postfix = ".HandlerInit"; 044 public static final String PLUGGER_INSTANCE = "main.Plugger"; 045 public HashMap<String, Long> versions = new HashMap<String, Long>(); 046 public HashMap<String, File> files = new HashMap<String, File>(); 047 public HashMap<String, Integer> mark = new HashMap<String, Integer>(); 048 public HashMap<String, String> initializer = new HashMap<String, String>(); 049 public HashMap<String, String> configxml = new HashMap<String, String>(); 050 public HashMap<String, ArrayList<Object[]>> depends = new HashMap<String, ArrayList<Object[]>>(); 051 public HashMap<String, ArrayList<String>> childs = new HashMap<String, ArrayList<String>>(); 052 public URLClassLoader classLoader = null; 053 public int activePlugins = 0; 054 055 private BlackBoard blackboard = null; 056 private String first = null; 057 058 public Plugger(BlackBoard blackboard) { 059 this.blackboard = blackboard; 060 blackboard.setData(PLUGGER_INSTANCE, this); 061 } 062 063 /** 064 * Search plugins directory and add jar files to Graphlab. 065 * And add all jar files in plgins directory and lib directory 066 * to <code>classLoader</code> 067 */ 068 public void plug() { 069 File f = new File("plugins"); 070 if (f.isDirectory() && f.canRead()) { 071 for (File ff : f.listFiles()) { 072 if (ff.isFile() && "jar".equalsIgnoreCase(getExtension(ff))) { 073 init(ff); 074 } 075 } 076 System.out.println("------------------------------------------------------------"); 077 verify(); 078 System.out.println("------------------------------------------------------------"); 079 if (first != null) { 080 int libCount = 0; 081 File libf = new File("lib"); 082 ArrayList<URL> libURLs = new ArrayList<URL>(); 083 if (libf.isDirectory() && libf.canRead()) { 084 for (File ff : libf.listFiles()) { 085 if (ff.isFile() && "jar".equalsIgnoreCase(getExtension(ff))) { 086 try { 087 libURLs.add(ff.toURL()); 088 System.out.println("Library file " + ff + " added."); 089 } catch (MalformedURLException e) { 090 e.printStackTrace(); 091 } 092 libCount++; 093 } 094 } 095 } 096 URL[] urls = new URL[activePlugins + libCount + 1]; 097 int i = 0; 098 for (URL libURL : libURLs) 099 urls[i++] = libURL; 100 for (String name : files.keySet()) { 101 if (mark.get(name) == 0) 102 try { 103 urls[i] = files.get(name).toURL(); 104 } catch (MalformedURLException e) { 105 System.out.println(name + " [" + files.get(name).getPath() + "]"); 106 e.printStackTrace(); 107 } 108 i++; 109 } 110 try { 111 urls[i] = new File("extensions").toURL(); 112 } catch (MalformedURLException e) { 113 e.printStackTrace(); 114 } 115 classLoader = new URLClassLoader(urls); 116 System.out.println("" + i + " jar file(s) loaded."); 117 System.out.println("------------------------------------------------------------"); 118 dfs(first); 119 } else 120 System.out.println("Can't Load Any Plugin!"); 121 System.out.println("------------------------------------------------------------"); 122 } else 123 System.out.println("There is no directory with name plugins."); 124 } 125 126 /** 127 * Read manifest of a jar file and make that plugin candidate 128 * to be loaded. 129 * 130 * @param ff file object 131 */ 132 public void init(File ff) { 133 try { 134 JarFile jf = new JarFile(ff); 135 System.out.println("------------------------------------------------------------"); 136 String name = jf.getManifest().getMainAttributes().getValue("plugin-name"); 137 String verStr = jf.getManifest().getMainAttributes().getValue("plugin-version"); 138 String dependsStr = jf.getManifest().getMainAttributes().getValue("plugin-depends"); 139 if (name == null || verStr == null) { 140 System.out.println("Skipping " + name + "(" + verStr + ")"); 141 return; 142 } 143 Long ver = Long.parseLong(verStr); 144 System.out.println("Detected " + name + "(" + ver + ") ..."); 145 for (Entry<Object, Object> s : jf.getManifest().getMainAttributes().entrySet()) { 146 if (!"plugin-name".equals(s.getKey()) && !"plugin-version".equals(s.getKey())) 147 System.out.println(s.getKey() + " : " + s.getValue()); 148 } 149 ArrayList<Object[]> dependsArray = new ArrayList<Object[]>(); 150 if (dependsStr != null) { 151 StringTokenizer st = new StringTokenizer(dependsStr); 152 while (st.hasMoreElements()) { 153 String depName = st.nextToken(); 154 String depVerStr = st.nextToken(); 155 Long depVer = Long.parseLong(depVerStr); 156 dependsArray.add(new Object[]{depName, depVer}); 157 } 158 } 159 versions.put(name, ver); 160 mark.put(name, -1); 161 files.put(name, ff); 162 childs.put(name, new ArrayList<String>()); 163 depends.put(name, dependsArray); 164 initializer.put(name, jf.getManifest().getMainAttributes().getValue("plugin-initializer")); 165 configxml.put(name, jf.getManifest().getMainAttributes().getValue("plugin-configxml")); 166 } catch (Exception ex) { 167 ex.printStackTrace(); 168 } 169 170 } 171 172 /** 173 * Remove a "plugin candidate" because of lack of dependencies. 174 * 175 * @param name Name of plugin 176 */ 177 public void remove(String name) { 178 if (mark.get(name) != -2) { 179 System.out.println("Removing " + name + " because of lack of dependencies..."); 180 mark.put(name, -2); 181 for (String ch : childs.get(name)) 182 remove(ch); 183 for (Object[] dep : depends.get(name)) 184 childs.get(dep[0]).remove(name); 185 } 186 } 187 188 /** 189 * Check dependencies 190 */ 191 public void verify() { 192 for (String name : versions.keySet()) { 193 for (Object[] dep : depends.get(name)) { 194 if (mark.get(name) == -1) { 195 String depName = (String) dep[0]; 196 Long depVer = (Long) dep[1]; 197 Long depPlugVer = (versions.get(depName) == null) ? null : (Long) versions.get(depName); 198 if (depPlugVer == null || depPlugVer < depVer || mark.get(depName) == -2) { 199 remove(name); 200 } else { 201 childs.get(depName).add(name); 202 } 203 } 204 } 205 if (mark.get(name) == -1) 206 mark.put(name, 0); 207 } 208 activePlugins = 0; 209 for (String name : versions.keySet()) { 210 System.out.println("Plugin " + name + "(" + mark.get(name) + ") has " + childs.get(name).size() + " child(ren)!"); 211 if (mark.get(name) == 0) { 212 activePlugins++; 213 if (first == null && childs.get(name).isEmpty()) 214 first = name; 215 } 216 } 217 } 218 219 /** 220 * DFS to find a topological sort of plugins. 221 * 222 * @param name Name of plugin 223 */ 224 public void dfs(String name) { 225 mark.put(name, 1); 226 System.out.println("DFS on " + name + " ..."); 227 for (Object[] dep : depends.get(name)) { 228 if (mark.get(dep[0]) == 0) 229 dfs((String) dep[0]); 230 } 231 mark.put(name, 2); 232 load(name); 233 mark.put(name, 3); 234 for (String chstr : childs.get(name)) { 235 if (mark.get(chstr) == 0) 236 dfs((String) chstr); 237 } 238 } 239 240 /** 241 * Load and initialize a plugin. 242 * if plugin has defined plugin-initializer or has 243 * graphlab.plugins.<i>pluginname</i>.Init, Then the 244 * init class of plugin will be loaded. 245 * Else if plugin has defined plugin-configxml 246 * (or using default = 247 * "/graphlab/gui/plugin/<i>pluginname</i>/config.xml"), 248 * then this function search parents of this plugin 249 * to find a <code>PluginHandlerInterface</code> and 250 * send path of config.xml to that handler. 251 * 252 * @param name Name of plugin 253 * @see PluginInterface#init(graphlab.platform.core.BlackBoard) 254 * @see PluginHandlerInterface#init(String,graphlab.platform.core.BlackBoard) 255 */ 256 public void load(String name) { 257 try { 258 System.out.println("Loading " + name + " ..."); 259 try { 260 String cName = initializer.get(name); 261 if (cName == null) 262 cName = prefix + name + postfix; 263 PluginInterface init = (PluginInterface) classLoader.loadClass(cName).newInstance(); 264 init.init(blackboard); 265 } catch (ClassNotFoundException cnfe) { 266 String confixml = configxml.get(name); 267 if (confixml == null) 268 confixml = "/graphlab/plugins/" + name + "/config.xml"; 269 Iterator<Object[]> it = depends.get(name).iterator(); 270 PluginHandlerInterface init = null; 271 while (init == null) { 272 try { 273 init = (PluginHandlerInterface) classLoader.loadClass(prefix + it.next()[0].toString() + handler_postfix).newInstance(); 274 init.init(confixml, blackboard); 275 } catch (ClassNotFoundException cnfe2) { 276 } 277 } 278 } 279 System.out.println("Loaded : " + name + "."); 280 } catch (Exception ex) { 281 ex.printStackTrace(); 282 } 283 } 284 285 /** 286 * Return the extension portion of the file's name . 287 * 288 * @see #getExtension 289 * @see javax.swing.filechooser.FileFilter#accept 290 */ 291 public static String getExtension(File f) { 292 if (f != null) { 293 String filename = f.getName(); 294 int i = filename.lastIndexOf('.'); 295 if (i > 0 && i < filename.length() - 1) { 296 return filename.substring(i + 1).toLowerCase(); 297 } 298 ; 299 } 300 return null; 301 } 302 }