1 /*
2 * (C) 2002 David Carr david@carr.name
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package net.sourceforge.mflow.impl;
20
21 import java.io.DataInputStream;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.net.MalformedURLException;
26 import java.net.URL;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.jar.Attributes;
33 import java.util.jar.JarEntry;
34 import java.util.jar.JarFile;
35 import java.util.jar.Manifest;
36 import java.util.logging.Logger;
37
38 import javax.swing.Icon;
39 import javax.swing.ImageIcon;
40 import javax.swing.JDialog;
41
42 import net.sourceforge.mflow.api.Plugin;
43 import net.sourceforge.mflow.api.PluginManager;
44 import net.sourceforge.mflow.reflect.ParamArgPair;
45 import net.sourceforge.mflow.reflect.ReflectionException;
46 import net.sourceforge.mflow.reflect.ReflectionUtil;
47 import net.sourceforge.mflow.util.IOUtil;
48
49 /***
50 * <p>Used to locate plugins within JAR files. Plugins must have an activator,
51 * which serves as the entry point to the plugin. All attributes of plugins are
52 * specified in the manifest of their JAR file.</p>
53 *
54 * <p>Example:
55 * <pre>Name: net.sourceforge.mflow.folder.sftp.Activator.class
56 * PluginName: SFTP Folder
57 * Type: Folder
58 * DescriptionFile: net/sourceforge/mflow/folder/sftp/description.txt
59 * IconFile: net/sourceforge/mflow/folder/sftp/images/icon.gif
60 * ConfigurationDialog: net.sourceforge.mflow.folder.sftp.ConfigurationDialog
61 * Version: 1.0</pre>
62 *
63 * <p>The <code>Name</code> field identifies the Activator class file in the
64 * JAR. The <code>PluginName</code> field provides a textual name for the
65 * plugin, and the <code>Type</code> field identifies the type of the plugin.
66 * These fields must be present in the entry, whereas the remaining fields are
67 * optional.</p>
68 *
69 * <p>The <code>DescriptionFile</code> field identifies a text file in the JAR
70 * that contains a detailed description of the plugin (and may optionally
71 * include a copyright message or other related information).
72 * <code>IconFile</code> identifies an icon image for the plugin.
73 * <code>ConfigurationDialog</code> is the fully-qualified name of a class
74 * within the JAR that provides a GUI for configuring the plugin.
75 * Finally, <code>Version</code> specifies the version number of the plugin.</p>
76 *
77 * <p>The <code>PluginLocator</code> reads a JAR Manifest, searching for entries
78 * that include a <code>PluginName</code> field. For each valid entry that is
79 * found, a <code>Plugin</code> object is constructed which encapsulates all
80 * of the information specified in the entry.</p>
81 *
82 * @author carrd
83 */
84 public class PluginLocator {
85 private static final Logger log =
86 Logger.getLogger(PluginLocator.class.getName());
87 private final PluginClassLoader loader = new PluginClassLoader();
88 private PluginManager pluginManager;
89
90 private URL[] convertFilesToURLs(List files) throws MalformedURLException {
91 URL[] ret = new URL[files.size()];
92 for (int i = 0; i < files.size(); i++) {
93 File file = (File) files.get(i);
94 String fileURL = file.toURL().toExternalForm();
95 URL url = new URL("jar:" + fileURL);
96 ret[i] = url;
97 }
98 return ret;
99 }
100
101 PluginLocator(PluginManager pluginManager) {
102 this.pluginManager = pluginManager;
103 }
104
105 /*
106 * Construct a plugin object from data obtained in the manifest.
107 */
108 private Plugin constructPlugin(
109 JarFile jar,
110 String className,
111 Attributes attrs) {
112 String name = null;
113 String type = null;
114 String description = null;
115 String descriptionFile = null;
116 String version = null;
117 String iconFile = null;
118 Icon icon = null;
119 String dialogClass = null;
120 JDialog dialog = null;
121 for (Iterator it = attrs.keySet().iterator(); it.hasNext();) {
122 Attributes.Name attrNameObj = (Attributes.Name) it.next();
123 String attrName = attrNameObj.toString();
124 String attrValue = attrs.getValue(attrName);
125 if (attrName.equals("PluginName")) {
126 name = attrValue;
127 } else if (attrName.equals("Type")) {
128 type = attrValue;
129 } else if (attrName.equals("DescriptionFile")) {
130 descriptionFile = attrValue;
131 } else if (attrName.equals("IconFile")) {
132 iconFile = attrValue;
133 } else if (attrName.equals("Version")) {
134 version = attrValue;
135 } else if (attrName.equals("ConfigurationDialog")) {
136 dialogClass = attrValue;
137 }
138 }
139 if ((type == null) || (name == null)) {
140 return null; //Can't construct a plugin without the mandatory attributes
141 }
142 icon = getIcon(jar, iconFile);
143 description = getDescription(jar, descriptionFile);
144 dialog = getDialog(dialogClass);
145 return new PluginImpl(
146 this.pluginManager,
147 className,
148 name,
149 type,
150 description,
151 version,
152 icon,
153 dialog);
154 }
155
156 private JDialog getDialog(String dialogClass) {
157 JDialog ret = null;
158 if (dialogClass != null) { //load the configuration dialog
159 Class[] paramType = new Class[] { PluginManager.class };
160 Object[] arg = new Object[] { this.pluginManager };
161 List sigs = new ArrayList();
162 ParamArgPair pair = new ParamArgPair(paramType, arg);
163 sigs.add(pair);
164 sigs.add(new ParamArgPair());
165 try {
166 ret = (JDialog) ReflectionUtil.instantiate(dialogClass, sigs);
167 } catch (ReflectionException re) {
168 log.throwing(getClass().getName(), "getDialog", re);
169 }
170 }
171 return ret;
172 }
173
174 private Icon getIcon(JarFile jar, String iconFile) {
175 if ((iconFile != null)) { //load the icon
176 JarEntry entry = (JarEntry) jar.getEntry(iconFile);
177 if (entry != null) {
178 InputStream is = null;
179 try {
180 is = jar.getInputStream(entry);
181 byte[] data = IOUtil.readStreamToByteArray(is);
182 return new ImageIcon(data);
183 } catch (IOException ioe) {
184 log.throwing(getClass().getName(), "getIcon", ioe);
185 } finally {
186 IOUtil.closeStream(is);
187 }
188 }
189 }
190 return null;
191 }
192
193 private String getDescription(JarFile jar, String descriptionFile) {
194 if (descriptionFile != null) { //load the description
195 JarEntry entry = (JarEntry) jar.getEntry(descriptionFile);
196 if (entry != null) {
197 InputStream is = null;
198 try {
199 is = jar.getInputStream(entry);
200 return IOUtil.readStreamToString(new DataInputStream(is));
201 } catch (IOException ioe) {
202 log.throwing(getClass().getName(), "getDescription", ioe);
203 } finally {
204 IOUtil.closeStream(is);
205 }
206 }
207 }
208 return null;
209 }
210
211 /***
212 * @param files
213 */
214 public void addFiles(List files) {
215 try {
216 URL[] urls = convertFilesToURLs(files);
217 this.loader.addURLs(urls);
218 } catch (MalformedURLException mue) {
219 log.throwing(getClass().getName(), "addFiles", mue);
220 }
221 }
222
223 /***
224 * Find all plugins in the specified JAR file.
225 *
226 * @param file The path of a JAR file.
227 * @return A (possibly empty) <code>Iterator</code> of <code>Plugin</code>
228 * objects representing the plugins found in the JAR file.
229 */
230 public Iterator findPlugins(File file) {
231 List entries = new ArrayList();
232 JarFile jarFile = null;
233 Map map = null;
234 try {
235 jarFile = new JarFile(file);
236 Manifest manifest = jarFile.getManifest();
237 map = manifest.getEntries();
238 } catch (IOException ioe) {
239 map = new HashMap(); //just return an empty iterator
240 }
241 for (Iterator it = map.keySet().iterator(); it.hasNext();) {
242 String classFile = (String) it.next();
243 Attributes attrs = (Attributes) map.get(classFile);
244 String className = PluginClassLoader.classPathToName(classFile);
245 Plugin p = constructPlugin(jarFile, className, attrs);
246 if (p != null) {
247 entries.add(p);
248 }
249 }
250 return entries.iterator();
251 }
252 }
This page was automatically generated by Maven