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 */
021 package csheets.ext;
022
023 import java.io.File;
024 import java.io.FileInputStream;
025 import java.io.IOException;
026 import java.io.InputStream;
027 import java.net.MalformedURLException;
028 import java.net.URL;
029 import java.net.URLClassLoader;
030 import java.util.Collection;
031 import java.util.Map;
032 import java.util.Properties;
033 import java.util.SortedMap;
034 import java.util.TreeMap;
035
036 import csheets.CleanSheets;
037
038 /**
039 * The class that manages extensions to the CleanSheets application.
040 * @author Einar Pehrson
041 */
042 public 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 }