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 }