source: trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/PluginLoader.java @ 2232

Last change on this file since 2232 was 2232, checked in by pharms, 7 years ago
  • solved some findbugs issues
  • Property svn:mime-type set to text/plain
File size: 7.8 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.plugin;
16
17import java.io.File;
18import java.io.FileInputStream;
19import java.io.FileNotFoundException;
20import java.io.FilenameFilter;
21import java.io.IOException;
22import java.lang.reflect.InvocationTargetException;
23import java.lang.reflect.Method;
24import java.net.MalformedURLException;
25import java.net.URL;
26import java.net.URLClassLoader;
27import java.util.Collection;
28import java.util.Collections;
29import java.util.LinkedList;
30import java.util.jar.JarInputStream;
31import java.util.jar.Manifest;
32
33/**
34 * <p>
35 * This class provides the functionality to load AutoQUEST plug-ins from a
36 * pre-defined folder.
37 * </p>
38 *
39 * @author Steffen Herbold
40 * @version 1.0
41 */
42public class PluginLoader {
43
44        /**
45         * <p>
46         * Handle of the plug-in directory.
47         * </p>
48         */
49        private final File pluginDir;
50
51        /**
52         * <p>
53         * Collection of the loaded plug-ins.
54         * </p>
55         */
56        private final Collection<AutoQUESTPlugin> plugins;
57
58        /**
59         * <p>
60         * Constructor. Creates a new PluginLoader that can load plug-ins the
61         * defined directory.
62         * </p>
63         *
64         * @param pluginDir
65         *            handle of the directory; in case the handle is
66         *            <code>null</code> or does not describe a directory, an
67         *            {@link IllegalArgumentException} is thrown
68         */
69        public PluginLoader(File pluginDir) {
70                if (pluginDir == null) {
71                        throw new IllegalArgumentException(
72                                        "Parameter pluginDir must not be null!");
73                }
74                if (!pluginDir.isDirectory()) {
75                        throw new IllegalArgumentException("File " + pluginDir.getPath()
76                                        + " is not a directory");
77                }
78                this.pluginDir = pluginDir;
79                plugins = new LinkedList<AutoQUESTPlugin>();
80        }
81
82        /**
83         * <p>
84         * Loads plug-ins from {@link #pluginDir}.
85         * </p>
86         *
87         * @throws PluginLoaderException
88         *             thrown if there is a problem loading a plug-in or updating
89         *             the classpath
90         */
91        public void load() throws PluginLoaderException {
92                File[] jarFiles = pluginDir.listFiles(new FilenameFilter() {
93                        @Override
94                        public boolean accept(File dir, String name) {
95                                return checkNameConformity(name);
96                        }
97                });
98
99                if (jarFiles != null) {
100                        for (File jarFile : jarFiles) {
101                                updateClassLoader(jarFile);
102
103                                String pluginName = jarFile.getName().split("-")[2];
104                                String pluginClassName = "de.ugoe.cs.autoquest.plugin." + pluginName
105                                                + "." + pluginName.toUpperCase() + "Plugin";
106
107                                Class<?> pluginClass = null;
108                                try {
109                                        pluginClass = Class.forName(pluginClassName);
110                                } catch (ClassNotFoundException e) {
111                                        throw new PluginLoaderException("No class '" + pluginClassName
112                                                        + "' found in " + pluginDir + "/" + jarFile.getName());
113                                }
114                                try {
115                                        AutoQUESTPlugin pluginObject = (AutoQUESTPlugin) pluginClass
116                                                        .newInstance();
117                                        plugins.add(pluginObject);
118                                } catch (InstantiationException e) {
119                                        throw new PluginLoaderException("Could not instantiate "
120                                                        + pluginClassName);
121                                } catch (IllegalAccessException e) {
122                                        throw new PluginLoaderException("Could not access "
123                                                        + pluginClassName);
124                                } catch (ClassCastException e) {
125                                        throw new PluginLoaderException("Class " + pluginClassName
126                                                        + " not instance of AutoQUESTPlugin");
127                                }
128                        }
129                }
130        }
131
132        /**
133         * <p>
134         * Retrieves the classpath from a Jar file's MANIFEST.
135         * </p>
136         */
137        protected String[] getClassPathFromJar(File jarFile) {
138                String[] classPath;
139
140                JarInputStream jarInputStream = null;
141                Manifest manifest = null;
142                try {
143                    FileInputStream fileStream = new FileInputStream(jarFile);
144                    try {
145                        jarInputStream = new JarInputStream(fileStream);
146                        manifest = jarInputStream.getManifest();
147                    } finally {
148                        jarInputStream.close();
149                        fileStream.close();
150                    }
151                } catch (FileNotFoundException e) {
152                        throw new AssertionError(
153                                        "FileNotFoundException should be impossible!");
154                } catch (IOException e) {
155                        throw new PluginLoaderException(e);
156                }
157
158                String jarClassPath = manifest.getMainAttributes().getValue(
159                                "Class-Path");
160
161                if (jarClassPath != null) {
162                        String[] jarClassPathElements = jarClassPath.split(" ");
163                        classPath = new String[jarClassPathElements.length];
164                        for (int i = 0; i < jarClassPathElements.length; i++) {
165                                classPath[i] = "file:"
166                                                + jarFile.getParentFile().getAbsolutePath() + "/"
167                                                + jarClassPathElements[i];
168                        }
169                        try {
170                                jarInputStream.close();
171                        } catch (IOException e) {
172                                throw new PluginLoaderException(e);
173                        }
174                } else {
175                        classPath = new String[] {};
176                }
177                return classPath;
178        }
179
180        /**
181         * <p>
182         * Updates the classpath of the {@link ClassLoader} to include the plug-in
183         * jar as well as further libraries required by the plug-in jar as defined
184         * in the <code>Class-Path</code> section of its manifest.
185         * </p>
186         *
187         * @param jarFile
188         *            handle of the plug-in jar file
189         * @throws PluginLoaderException
190         *             thrown if there is a problem updating the class loader or
191         *             loading the plug-in jar
192         */
193        private void updateClassLoader(File jarFile) throws PluginLoaderException {
194                String[] classPath = getClassPathFromJar(jarFile);
195                URLClassLoader classLoader = (URLClassLoader) ClassLoader
196                                .getSystemClassLoader();
197                Method method;
198
199                try {
200                        method = URLClassLoader.class.getDeclaredMethod("addURL",
201                                        new Class[] { URL.class });
202                } catch (SecurityException e) {
203                        throw new PluginLoaderException(
204                                        "addURL method of URLClassLoader not accessible via reflection.");
205                } catch (NoSuchMethodException e) {
206                        throw new AssertionError(
207                                        "URLClassLoader does not have addURL method. Should be impossible!!");
208                }
209                method.setAccessible(true);
210
211                try {
212                        method.invoke(
213                                        classLoader,
214                                        new Object[] { new URL("file:" + jarFile.getAbsoluteFile()) });
215                        for (String element : classPath) {
216                                method.invoke(classLoader, new Object[] { new URL(element) });
217                        }
218                } catch (IllegalArgumentException e) {
219                        throw new AssertionError(
220                                        "Illegal arguments for addURL method. Should be impossible!!");
221                } catch (MalformedURLException e) {
222                        throw new PluginLoaderException(e);
223                } catch (IllegalAccessException e) {
224                        throw new PluginLoaderException(
225                                        "addURL method of URLClassLoader not accessible via reflection.");
226                } catch (InvocationTargetException e) {
227                        throw new PluginLoaderException(e);
228                }
229        }
230
231        /**
232         * <p>
233         * Checks if the name of a file indicates that it defines a AutoQUEST plug-in.
234         * The structure of valid plug-in filenames is
235         * <code>autoquest-plugin-%PLUGIN_NAME%-version.jar</code>, where
236         * <code>%PLUGIN_NAME%</code> is replaced by the name of the plug-in. Note
237         * that plug-in names must not contain any dashes.
238         * </p>
239         *
240         * @param filename
241         *            filename that is checked
242         * @return true if filename matches pattern of AutoQUEST plug-in; false
243         *         otherwise
244         */
245        protected boolean checkNameConformity(String filename) {
246                if (filename == null) {
247                        return false;
248                }
249                return filename.startsWith("autoquest-plugin-") && !filename.startsWith("autoquest-plugin-core")
250                                &&
251                                ((filename.split("-").length == 4 && filename.endsWith(".jar")) ||
252                                  filename.split("-").length == 5 && filename.endsWith("SNAPSHOT.jar") ||
253                                  filename.split("-").length == 6 && filename.endsWith(".jar"));
254        }
255       
256        public Collection<AutoQUESTPlugin> getPlugins() {
257                return Collections.unmodifiableCollection(plugins);
258        }
259}
Note: See TracBrowser for help on using the repository browser.