Changeset 2260 for trunk/autoquest-plugin-core/src/main/java/de/ugoe
- Timestamp:
- 08/01/19 12:11:21 (5 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/autoquest-plugin-core/src/main/java/de/ugoe/cs/autoquest/plugin/PluginLoader.java
r2232 r2260 16 16 17 17 import java.io.File; 18 import java.io.FileInputStream;19 import java.io.FileNotFoundException;20 18 import java.io.FilenameFilter; 21 import java.io.IOException;22 19 import java.lang.reflect.InvocationTargetException; 23 import java.lang.reflect.Method;24 20 import java.net.MalformedURLException; 25 21 import java.net.URL; … … 28 24 import java.util.Collections; 29 25 import java.util.LinkedList; 30 import java.util.jar.JarInputStream; 31 import java.util.jar.Manifest; 26 import java.util.List; 32 27 33 28 /** 34 29 * <p> 35 * This class provides the functionality to load AutoQUEST plug-ins from a 36 * pre-defined folder. 30 * This class provides the functionality to load AutoQUEST plug-ins from a pre-defined folder. 37 31 * </p> 38 32 * … … 40 34 * @version 1.0 41 35 */ 42 public 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 } 36 public class PluginLoader implements AutoCloseable { 37 38 /** 39 * <p> 40 * Handle of the plug-in directory. 41 * </p> 42 */ 43 private final File pluginDir; 44 45 /** 46 * <p> 47 * Collection of the loaded plug-ins. 48 * </p> 49 */ 50 private final Collection<AutoQUESTPlugin> plugins; 51 52 /** 53 * <p> 54 * the class loaders instantiated internally 55 * </p> 56 */ 57 private List<URLClassLoader> instantiatedClassLoaders = new LinkedList<>(); 58 59 /** 60 * <p> 61 * Constructor. Creates a new PluginLoader that can load plug-ins the defined directory. 62 * </p> 63 * 64 * @param pluginDir 65 * handle of the directory; in case the handle is <code>null</code> or does not 66 * describe a directory, an {@link IllegalArgumentException} is thrown 67 */ 68 public PluginLoader(File pluginDir) { 69 if (pluginDir == null) { 70 throw new IllegalArgumentException("Parameter pluginDir must not be null!"); 71 } 72 if (!pluginDir.isDirectory()) { 73 throw new IllegalArgumentException("File " + pluginDir.getPath() + 74 " is not a directory"); 75 } 76 this.pluginDir = pluginDir; 77 plugins = new LinkedList<AutoQUESTPlugin>(); 78 } 79 80 /** 81 * <p> 82 * Loads plug-ins from {@link #pluginDir}. 83 * </p> 84 * 85 * @throws PluginLoaderException 86 * thrown if there is a problem loading a plug-in or updating the classpath 87 */ 88 public void load() throws PluginLoaderException { 89 File[] jarFiles = pluginDir.listFiles(new FilenameFilter() { 90 @Override 91 public boolean accept(File dir, String name) { 92 return checkNameConformity(name); 93 } 94 }); 95 96 if (jarFiles != null) { 97 for (File jarFile : jarFiles) { 98 updateClassLoader(jarFile); 99 100 String pluginName = jarFile.getName().split("-")[2]; 101 String pluginClassName = "de.ugoe.cs.autoquest.plugin." + pluginName + "." + 102 pluginName.toUpperCase() + "Plugin"; 103 104 Class<?> pluginClass = null; 105 try { 106 pluginClass = Class.forName(pluginClassName); 107 } 108 catch (ClassNotFoundException e) { 109 throw new PluginLoaderException("No class '" + pluginClassName + "' found in " + 110 pluginDir + "/" + jarFile.getName()); 111 } 112 try { 113 AutoQUESTPlugin pluginObject = 114 (AutoQUESTPlugin) pluginClass.getDeclaredConstructor().newInstance(); 115 plugins.add(pluginObject); 116 } 117 catch (InstantiationException e) { 118 throw new PluginLoaderException("Could not instantiate " + pluginClassName); 119 } 120 catch (IllegalAccessException e) { 121 throw new PluginLoaderException("Could not access " + pluginClassName); 122 } 123 catch (ClassCastException e) { 124 throw new PluginLoaderException("Class " + pluginClassName + 125 " not instance of AutoQUESTPlugin"); 126 } 127 catch (IllegalArgumentException e) { 128 // TODO Auto-generated catch block 129 e.printStackTrace(); 130 } 131 catch (InvocationTargetException e) { 132 // TODO Auto-generated catch block 133 e.printStackTrace(); 134 } 135 catch (NoSuchMethodException e) { 136 // TODO Auto-generated catch block 137 e.printStackTrace(); 138 } 139 catch (SecurityException e) { 140 // TODO Auto-generated catch block 141 e.printStackTrace(); 142 } 143 } 144 } 145 } 146 147 /** 148 * <p> 149 * Retrieves the classpath from a Jar file's MANIFEST. 150 * </p> 151 */ 152 /* 153 * protected String[] getClassPathFromJar(File jarFile) { String[] classPath; 154 * 155 * JarInputStream jarInputStream = null; Manifest manifest = null; try { FileInputStream 156 * fileStream = new FileInputStream(jarFile); try { jarInputStream = new 157 * JarInputStream(fileStream); manifest = jarInputStream.getManifest(); } finally { 158 * jarInputStream.close(); fileStream.close(); } } catch (FileNotFoundException e) { throw new 159 * AssertionError( "FileNotFoundException should be impossible!"); } catch (IOException e) { 160 * throw new PluginLoaderException(e); } 161 * 162 * String jarClassPath = manifest.getMainAttributes().getValue( "Class-Path"); 163 * 164 * if (jarClassPath != null) { String[] jarClassPathElements = jarClassPath.split(" "); 165 * classPath = new String[jarClassPathElements.length]; for (int i = 0; i < 166 * jarClassPathElements.length; i++) { classPath[i] = "file:" + 167 * jarFile.getParentFile().getAbsolutePath() + "/" + jarClassPathElements[i]; } try { 168 * jarInputStream.close(); } catch (IOException e) { throw new PluginLoaderException(e); } } 169 * else { classPath = new String[] {}; } return classPath; } 170 */ 171 172 /** 173 * <p> 174 * Updates the classpath of the {@link ClassLoader} to include the plug-in jar as well as 175 * further libraries required by the plug-in jar as defined in the <code>Class-Path</code> 176 * section of its manifest. 177 * </p> 178 * 179 * @param jarFile 180 * handle of the plug-in jar file 181 * @throws PluginLoaderException 182 * thrown if there is a problem updating the class loader or loading the plug-in jar 183 */ 184 private void updateClassLoader(File jarFile) throws PluginLoaderException { 185 // String[] classPath = getClassPathFromJar(jarFile); 186 187 try { 188 URLClassLoader loader = new URLClassLoader(new URL[] 189 { new URL("file:" + jarFile.getAbsoluteFile()) }, 190 ClassLoader.getSystemClassLoader()); 191 192 instantiatedClassLoaders.add(loader); 193 } 194 catch (MalformedURLException e) { 195 throw new PluginLoaderException(e); 196 } 197 } 198 199 /** 200 * <p> 201 * Checks if the name of a file indicates that it defines a AutoQUEST plug-in. The structure of 202 * valid plug-in filenames is <code>autoquest-plugin-%PLUGIN_NAME%-version.jar</code>, where 203 * <code>%PLUGIN_NAME%</code> is replaced by the name of the plug-in. Note that plug-in names 204 * must not contain any dashes. 205 * </p> 206 * 207 * @param filename 208 * filename that is checked 209 * @return true if filename matches pattern of AutoQUEST plug-in; false otherwise 210 */ 211 protected boolean checkNameConformity(String filename) { 212 if (filename == null) { 213 return false; 214 } 215 return filename.startsWith("autoquest-plugin-") && 216 !filename.startsWith("autoquest-plugin-core") && 217 ((filename.split("-").length == 4 && filename.endsWith(".jar")) || 218 filename.split("-").length == 5 && filename.endsWith("SNAPSHOT.jar") || 219 filename.split("-").length == 6 && filename.endsWith(".jar")); 220 } 221 222 public Collection<AutoQUESTPlugin> getPlugins() { 223 return Collections.unmodifiableCollection(plugins); 224 } 225 226 @Override 227 public void close() throws Exception { 228 for (URLClassLoader loader : instantiatedClassLoaders) { 229 loader.close(); 230 } 231 } 232 259 233 }
Note: See TracChangeset
for help on using the changeset viewer.