001 package org.maltparser.core.plugin; 002 003 import java.io.BufferedInputStream; 004 import java.io.ByteArrayOutputStream; 005 import java.io.File; 006 import java.io.IOException; 007 import java.lang.reflect.InvocationTargetException; 008 import java.lang.reflect.Method; 009 import java.net.MalformedURLException; 010 import java.net.URL; 011 import java.net.URLClassLoader; 012 import java.security.SecureClassLoader; 013 import java.util.HashMap; 014 import java.util.HashSet; 015 import java.util.TreeSet; 016 import java.util.jar.Attributes; 017 import java.util.jar.JarEntry; 018 import java.util.jar.JarFile; 019 import java.util.jar.JarInputStream; 020 import java.util.jar.Manifest; 021 import java.util.regex.PatternSyntaxException; 022 023 import org.maltparser.core.exception.MaltChainedException; 024 import org.maltparser.core.options.OptionManager; 025 026 027 /** 028 The jar class loader loads the content of a jar file that complies with a MaltParser Plugin. 029 030 @author Johan Hall 031 */ 032 public class JarLoader extends SecureClassLoader { 033 private HashMap<String, byte[]> classByteArrays; 034 private HashMap<String, Class<?>> classes; 035 036 /** 037 * Creates a new class loader that is specialized for loading jar files. 038 * 039 * @param parent The parent class loader 040 */ 041 public JarLoader(ClassLoader parent) { 042 super(parent); 043 classByteArrays = new HashMap<String, byte[]>(); 044 classes = new HashMap<String, Class<?>>(); 045 } 046 047 /* (non-Javadoc) 048 * @see java.lang.ClassLoader#findClass(java.lang.String) 049 */ 050 protected Class<?> findClass(String name) { 051 String urlName = name.replace('.', '/'); 052 byte buf[]; 053 054 SecurityManager sm = System.getSecurityManager(); 055 if (sm != null) { 056 int i = name.lastIndexOf('.'); 057 if (i >= 0) { 058 sm.checkPackageDefinition(name.substring(0, i)); 059 } 060 } 061 062 buf = (byte[]) classByteArrays.get(urlName); 063 if (buf != null) { 064 return defineClass(null, buf, 0, buf.length); 065 } 066 return null; 067 } 068 069 /** 070 * Loads the content of a jar file that comply with a MaltParser Plugin 071 * 072 * @param jarUrl The URL to the jar file 073 * @throws PluginException 074 */ 075 public boolean readJarFile(URL jarUrl) throws MaltChainedException { 076 JarInputStream jis; 077 JarEntry je; 078 HashSet<URL> pluginXMLs = new HashSet<URL>(); 079 080 /*if (logger.isDebugEnabled()) { 081 logger.debug("Loading jar " + jarUrl+"\n"); 082 }*/ 083 JarFile jarFile; 084 try { 085 jarFile = new JarFile(jarUrl.getFile()); 086 } catch (IOException e) { 087 throw new PluginException("Could not open jar file " + jarUrl+". ", e); 088 } 089 try { 090 Manifest manifest = jarFile.getManifest(); 091 if (manifest != null) { 092 Attributes manifestAttributes = manifest.getMainAttributes(); 093 if (!(manifestAttributes.getValue("MaltParser-Plugin") != null && manifestAttributes.getValue("MaltParser-Plugin").equals("true"))) { 094 return false; 095 } 096 if (manifestAttributes.getValue("Class-Path") != null) { 097 String[] classPathItems = manifestAttributes.getValue("Class-Path").split(" "); 098 for (int i=0; i < classPathItems.length; i++) { 099 URL u; 100 try { 101 u = new URL(jarUrl.getProtocol()+":"+new File(jarFile.getName()).getParentFile().getPath()+"/"+classPathItems[i]); 102 } catch (MalformedURLException e) { 103 throw new PluginException("The URL to the plugin jar-class-path '"+jarUrl.getProtocol()+":"+new File(jarFile.getName()).getParentFile().getPath()+"/"+classPathItems[i]+"' is wrong. ", e); 104 } 105 URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader(); 106 Class<?> sysclass = URLClassLoader.class; 107 Method method = sysclass.getDeclaredMethod("addURL",new Class[]{URL.class}); 108 method.setAccessible(true); 109 method.invoke(sysloader,new Object[]{u }); 110 } 111 } 112 } 113 } catch (PatternSyntaxException e) { 114 throw new PluginException("Could not split jar-class-path entries in the jar-file '"+jarFile.getName()+"'. ", e); 115 } catch (IOException e) { 116 throw new PluginException("Could not read the manifest file in the jar-file '"+jarFile.getName()+"'. ", e); 117 } catch (NoSuchMethodException e) { 118 throw new PluginException("", e); 119 } catch (IllegalAccessException e) { 120 throw new PluginException("", e); 121 } catch (InvocationTargetException e) { 122 throw new PluginException("", e); 123 } 124 125 try { 126 jis = new JarInputStream(jarUrl.openConnection().getInputStream()); 127 128 while ((je = jis.getNextJarEntry()) != null) { 129 String jarName = je.getName(); 130 if (jarName.endsWith(".class")) { 131 /* if (logger.isDebugEnabled()) { 132 logger.debug(" Loading class: " + jarName+"\n"); 133 }*/ 134 loadClassBytes(jis, jarName); 135 Class<?> clazz = findClass(jarName.substring(0, jarName.length() - 6)); 136 classes.put(jarName.substring(0, jarName.length() - 6).replace('/','.'), clazz); 137 loadClass(jarName.substring(0, jarName.length() - 6).replace('/', '.')); 138 } 139 if (jarName.endsWith("plugin.xml")) { 140 pluginXMLs.add(new URL("jar:"+jarUrl.getProtocol()+":"+jarUrl.getPath()+"!/"+jarName)); 141 } 142 jis.closeEntry(); 143 } 144 for (URL url : pluginXMLs) { 145 /* if (logger.isDebugEnabled()) { 146 logger.debug(" Loading "+url+"\n"); 147 }*/ 148 OptionManager.instance().loadOptionDescriptionFile(url); 149 } 150 } catch (MalformedURLException e) { 151 throw new PluginException("The URL to the plugin.xml is wrong. ", e); 152 } catch (IOException e) { 153 throw new PluginException("cannot open jar file " + jarUrl+". ", e); 154 } catch (ClassNotFoundException e) { 155 throw new PluginException("The class "+e.getMessage() +" can't be found. ", e); 156 } 157 return true; 158 } 159 160 /** 161 * Returns the Class object for the class with the specified name. 162 * 163 * @param classname the fully qualified name of the desired class 164 * @return the Class object for the class with the specified name. 165 */ 166 public Class<?> getClass(String classname) { 167 return (Class<?>)classes.get(classname); 168 } 169 170 /** 171 * Reads a jar file entry into a byte array. 172 * 173 * @param jis The jar input stream 174 * @param jarName The name of a jar file entry 175 * @throws PluginException 176 */ 177 private void loadClassBytes(JarInputStream jis, String jarName) throws MaltChainedException { 178 BufferedInputStream jarBuf = new BufferedInputStream(jis); 179 ByteArrayOutputStream jarOut = new ByteArrayOutputStream(); 180 int b; 181 try { 182 while ((b = jarBuf.read()) != -1) { 183 jarOut.write(b); 184 } 185 classByteArrays.put(jarName.substring(0, jarName.length() - 6), jarOut.toByteArray()); 186 } catch (IOException e) { 187 throw new PluginException("Error reading entry " + jarName+". ", e); 188 } 189 } 190 191 /** 192 * Checks package access 193 * 194 * @param name the package name 195 */ 196 protected void checkPackageAccess(String name) { 197 SecurityManager sm = System.getSecurityManager(); 198 if (sm != null) { 199 sm.checkPackageAccess(name); 200 } 201 } 202 203 /* (non-Javadoc) 204 * @see java.lang.Object#toString() 205 */ 206 public String toString() { 207 StringBuilder sb = new StringBuilder(); 208 209 sb.append("The MaltParser Plugin Loader (JarLoader)\n"); 210 sb.append("---------------------------------------------------------------------\n"); 211 for (String entry : new TreeSet<String>(classes.keySet())) { 212 sb.append(" "+entry+"\n"); 213 } 214 return sb.toString(); 215 } 216 }