001/* 002 * Copyright (c) 2005 Einar Pehrson <einar@pehrson.nu>. 003 * 004 * This file is part of 005 * CleanSheets - a spreadsheet application for the Java platform. 006 * 007 * CleanSheets is free software; you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published by 009 * the Free Software Foundation; either version 2 of the License, or 010 * (at your option) any later version. 011 * 012 * CleanSheets is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with CleanSheets; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 */ 021package csheets.ext; 022 023import java.io.File; 024import java.io.FileInputStream; 025import java.io.IOException; 026import java.io.InputStream; 027import java.net.MalformedURLException; 028import java.net.URL; 029import java.net.URLClassLoader; 030import java.util.Collection; 031import java.util.Map; 032import java.util.Properties; 033import java.util.SortedMap; 034import java.util.TreeMap; 035 036import csheets.CleanSheets; 037 038/** 039 * The class that manages extensions to the CleanSheets application. 040 * @author Einar Pehrson 041 */ 042public class ExtensionManager { 043 044 /** The singleton instance */ 045 private static final ExtensionManager instance = new ExtensionManager(); 046 047 /** The name of the files in which extension properties are stored */ 048 private static final String PROPERTIES_FILENAME = "extensions.props"; 049 050 /** The extensions that have been loaded */ 051 private SortedMap<String, Extension> extensionMap 052 = new TreeMap<String, Extension>(); 053 054 /** The class loader used to load extensions */ 055 private Loader loader = new Loader(); 056 057 /** 058 * Creates the extension manager. 059 */ 060 private ExtensionManager() { 061 // Loads default extension properties 062 Properties extProps = new Properties(); 063 InputStream stream = CleanSheets.class.getResourceAsStream("res/" + PROPERTIES_FILENAME); 064 if (stream != null) 065 try { 066 extProps.load(stream); 067 } catch (IOException e) { 068 System.err.println("Could not load default extension properties from: " 069 + PROPERTIES_FILENAME); 070 } finally { 071 try { 072 if (stream != null) 073 stream.close(); 074 } catch (IOException e) {} 075 } 076 077 // Loads user extension properties 078 File userExtPropsFile = new File(PROPERTIES_FILENAME); 079 if (userExtPropsFile.exists()) 080 stream = null; 081 try { 082 stream = new FileInputStream(userExtPropsFile); 083 extProps.load(stream); 084 } catch (IOException e) { 085 } finally { 086 try { 087 if (stream != null) 088 stream.close(); 089 } catch (IOException e) {} 090 } 091 092 // Loads extensions 093 for (Map.Entry<Object, Object> entry : extProps.entrySet()) { 094 // Resolves class path 095 String classPathProp = (String)entry.getValue(); 096 URL classPath = null; 097 if (classPathProp.length() > 0) { 098 // Looks for resource 099 classPath = ExtensionManager.class.getResource(classPathProp); 100 if (classPath == null) { 101 // Looks for file 102 File classPathFile = new File(classPathProp); 103 if (classPathFile.exists()) 104 try { 105 classPath = classPathFile.toURL(); 106 } catch (MalformedURLException e) {} 107 } 108 } 109 110 // Loads class 111 String className = (String)entry.getKey(); 112 if (classPath == null) 113 load(className); 114 else 115 load(className, classPath); 116 } 117 } 118 119 /** 120 * Returns the singleton instance. 121 * @return the singleton instance 122 */ 123 public static ExtensionManager getInstance() { 124 return instance; 125 } 126 127 /** 128 * Returns the extensions that have been loaded. 129 * @return the extensions that have been loaded 130 */ 131 public Extension[] getExtensions() { 132 Collection<Extension> extensions = extensionMap.values(); 133 return extensions.toArray(new Extension[extensions.size()]); 134 } 135 136 /** 137 * Returns the extension with the given name. 138 * @return the extension with the given name or null if none was found 139 */ 140 public Extension getExtension(String name) { 141 return extensionMap.get(name); 142 } 143 144 /** 145 * Adds the given url to the class path, and loads the extension with the 146 * given class name. 147 * @param className the complete class name of a class that extends 148 * the abstract Extension class 149 * @param url the URL of the JAR-file or directory that contains the class 150 * @return the extension that was loaded, or null if none was found. 151 */ 152 public Extension load(String className, URL url) { 153 loader.addURL(url); 154 try { 155 Class extensionClass = Class.forName(className, true, loader); 156 return load(extensionClass); 157 } catch (Exception e) { 158 System.err.println("Failed to load extension class " + className + "."); 159 return null; 160 } 161 } 162 163 /** 164 * Loads the extension with the given class name. 165 * @param className the complete class name of a class that extends 166 * the abstract Extension class 167 * @return the extension that was loaded, or null if none was found. 168 */ 169 public Extension load(String className) { 170 try { 171 Class extensionClass = Class.forName(className); 172 return load(extensionClass); 173 } catch (Exception e) { 174 System.err.println("Failed to load extension class " + className + "."); 175 return null; 176 } 177 } 178 179 /** 180 * Instantiates the given extension class. 181 * @param extensionClass a class that extends the abstract Extension class 182 * @return the extension that was loaded, or null if none was found. 183 */ 184 public Extension load(Class extensionClass) { 185 try { 186 Extension extension = (Extension)extensionClass.newInstance(); 187 extensionMap.put(extension.getName(), extension); 188 return extension; 189 } catch (IllegalAccessException iae) { 190 System.err.println("Could not access extension " + extensionClass.getName() + "."); 191 return null; 192 } catch (InstantiationException ie) { 193 System.err.println("Could not load extension from " + extensionClass.getName() + "."); 194 ie.printStackTrace(); 195 return null; 196 } 197 } 198 199 /** 200 * Returns the class loader used to load extensions. 201 * @return the class loader used to load extensions 202 */ 203 public ClassLoader getLoader() { 204 return loader; 205 } 206 207 /** 208 * The class loader used to load extensions. 209 */ 210 public static class Loader extends URLClassLoader { 211 212 /** 213 * Creates a new extension loader. 214 */ 215 public Loader() { 216 super(new URL[]{}, Loader.class.getClassLoader()); 217 } 218 219 /** 220 * Appends the specified URL to the list of URLs to search for classes 221 * and resources. 222 * @param url the URL to be added to the search path of URL:s 223 */ 224 protected void addURL(URL url) { 225 super.addURL(url); 226 } 227 } 228}