    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    }