package org.maltparser.core.plugin;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.SecureClassLoader;
import java.util.HashMap;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.*;
import java.util.regex.PatternSyntaxException;
import org.maltparser.core.exception.MaltChainedException;
import org.maltparser.core.helper.HashSet;
import org.maltparser.core.options.OptionManager;
/**
* The jar class loader loads the content of a jar file that complies with a
* MaltParser Plugin.
*
* @author Johan Hall
*/
public class JarLoader extends SecureClassLoader {
private HashMap<String, byte[]> classByteArrays;
private HashMap<String, Class<?>> classes;
/**
* Creates a new class loader that is specialized for loading jar files.
*
* @param parent The parent class loader
*/
public JarLoader(ClassLoader parent) {
super(parent);
classByteArrays = new HashMap<String, byte[]>();
classes = new HashMap<String, Class<?>>();
}
/*
* (non-Javadoc) @see java.lang.ClassLoader#findClass(java.lang.String)
*/
@Override
protected Class<?> findClass(String name) {
String urlName = name.replace('.', '/');
byte buf[];
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
sm.checkPackageDefinition(name.substring(0, i));
}
}
buf = (byte[]) classByteArrays.get(urlName);
if (buf != null) {
return defineClass(null, buf, 0, buf.length);
}
return null;
}
/**
* Loads the content of a jar file that comply with a MaltParser Plugin
*
* @param jarUrl The URL to the jar file
* @throws PluginException
*/
public boolean readJarFile(URL jarUrl) throws MaltChainedException {
JarInputStream jis;
JarEntry je;
Set<URL> pluginXMLs = new HashSet<URL>();
/*
* if (logger.isDebugEnabled()) { logger.debug("Loading jar " +
* jarUrl+"\n");
}
*/
JarFile jarFile;
try {
jarFile = new JarFile(jarUrl.getFile());
} catch (IOException e) {
throw new PluginException("Could not open jar file " + jarUrl + ". ", e);
}
try {
Manifest manifest = jarFile.getManifest();
if (manifest != null) {
Attributes manifestAttributes = manifest.getMainAttributes();
if (!(manifestAttributes.getValue("MaltParser-Plugin") != null && manifestAttributes.getValue("MaltParser-Plugin").equals("true"))) {
return false;
}
if (manifestAttributes.getValue("Class-Path") != null) {
String[] classPathItems = manifestAttributes.getValue("Class-Path").split(" ");
for (int i = 0; i < classPathItems.length; i++) {
URL u;
try {
u = new URL(jarUrl.getProtocol() + ":" + new File(jarFile.getName()).getParentFile().getPath() + "/" + classPathItems[i]);
} catch (MalformedURLException e) {
throw new PluginException("The URL to the plugin jar-class-path '" + jarUrl.getProtocol() + ":" + new File(jarFile.getName()).getParentFile().getPath() + "/" + classPathItems[i] + "' is wrong. ", e);
}
URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<?> sysclass = URLClassLoader.class;
Method method = sysclass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(sysloader, new Object[]{u});
}
}
}
} catch (PatternSyntaxException e) {
throw new PluginException("Could not split jar-class-path entries in the jar-file '" + jarFile.getName() + "'. ", e);
} catch (IOException e) {
throw new PluginException("Could not read the manifest file in the jar-file '" + jarFile.getName() + "'. ", e);
} catch (NoSuchMethodException e) {
throw new PluginException("", e);
} catch (IllegalAccessException e) {
throw new PluginException("", e);
} catch (InvocationTargetException e) {
throw new PluginException("", e);
}
try {
jis = new JarInputStream(jarUrl.openConnection().getInputStream());
while ((je = jis.getNextJarEntry()) != null) {
String jarName = je.getName();
if (jarName.endsWith(".class")) {
/*
* if (logger.isDebugEnabled()) { logger.debug(" Loading
* class: " + jarName+"\n");
}
*/
loadClassBytes(jis, jarName);
Class<?> clazz = findClass(jarName.substring(0, jarName.length() - 6));
classes.put(jarName.substring(0, jarName.length() - 6).replace('/', '.'), clazz);
loadClass(jarName.substring(0, jarName.length() - 6).replace('/', '.'));
}
if (jarName.endsWith("plugin.xml")) {
pluginXMLs.add(new URL("jar:" + jarUrl.getProtocol() + ":" + jarUrl.getPath() + "!/" + jarName));
}
jis.closeEntry();
}
for (URL url : pluginXMLs) {
/*
* if (logger.isDebugEnabled()) { logger.debug(" Loading
* "+url+"\n");
}
*/
OptionManager.instance().loadOptionDescriptionFile(url);
}
} catch (MalformedURLException e) {
throw new PluginException("The URL to the plugin.xml is wrong. ", e);
} catch (IOException e) {
throw new PluginException("cannot open jar file " + jarUrl + ". ", e);
} catch (ClassNotFoundException e) {
throw new PluginException("The class " + e.getMessage() + " can't be found. ", e);
}
return true;
}
/**
* Returns the Class object for the class with the specified name.
*
* @param classname the fully qualified name of the desired class
* @return the Class object for the class with the specified name.
*/
public Class<?> getClass(String classname) {
return (Class<?>) classes.get(classname);
}
/**
* Reads a jar file entry into a byte array.
*
* @param jis The jar input stream
* @param jarName The name of a jar file entry
* @throws PluginException
*/
private void loadClassBytes(JarInputStream jis, String jarName) throws MaltChainedException {
BufferedInputStream jarBuf = new BufferedInputStream(jis);
ByteArrayOutputStream jarOut = new ByteArrayOutputStream();
int b;
try {
while ((b = jarBuf.read()) != -1) {
jarOut.write(b);
}
classByteArrays.put(jarName.substring(0, jarName.length() - 6), jarOut.toByteArray());
} catch (IOException e) {
throw new PluginException("Error reading entry " + jarName + ". ", e);
}
}
/**
* Checks package access
*
* @param name the package name
*/
protected void checkPackageAccess(String name) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPackageAccess(name);
}
}
/*
* (non-Javadoc) @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("The MaltParser Plugin Loader (JarLoader)\n");
sb.append("---------------------------------------------------------------------\n");
for (String entry : new TreeSet<String>(classes.keySet())) {
sb.append(" ").append(entry).append("\n");
}
return sb.toString();
}
}