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