PageRenderTime 56ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/JARClassLoader.java

#
Java | 439 lines | 301 code | 54 blank | 84 comment | 51 complexity | 2231d81ab28d2c340b01d805dcde7937 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * JARClassLoader.java - Loads classes from JAR files
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2003 Slava Pestov
  7. * Portions copyright (C) 1999 mike dillon
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit;
  24. //{{{ Imports
  25. import java.io.InputStream;
  26. import java.io.IOException;
  27. import java.net.URL;
  28. import java.util.*;
  29. import java.util.zip.ZipEntry;
  30. import java.util.zip.ZipFile;
  31. import org.gjt.sp.util.Log;
  32. import java.util.jar.Manifest;
  33. import java.util.jar.JarFile;
  34. import java.net.MalformedURLException;
  35. import java.util.jar.Attributes;
  36. import java.util.jar.Attributes.Name;
  37. //}}}
  38. /**
  39. * A class loader implementation that loads classes from JAR files. All
  40. * instances share the same set of classes.
  41. * @author Slava Pestov
  42. * @version $Id: JARClassLoader.java 5327 2006-01-01 18:40:29Z ezust $
  43. */
  44. public class JARClassLoader extends ClassLoader
  45. {
  46. //{{{ JARClassLoader constructor
  47. /**
  48. * This constructor creates a class loader for loading classes from all
  49. * plugins. For example BeanShell uses one of these so that scripts can
  50. * use plugin classes.
  51. */
  52. public JARClassLoader()
  53. {
  54. // for debugging
  55. id = INDEX++;
  56. live++;
  57. } //}}}
  58. //{{{ loadClass() method
  59. /**
  60. * @exception ClassNotFoundException if the class could not be found
  61. */
  62. public Class loadClass(String clazz, boolean resolveIt)
  63. throws ClassNotFoundException
  64. {
  65. // see what JARClassLoader this class is in
  66. Object obj = classHash.get(clazz);
  67. if(obj == NO_CLASS)
  68. {
  69. // we remember which classes we don't exist
  70. // because BeanShell tries loading all possible
  71. // <imported prefix>.<class name> combinations
  72. throw new ClassNotFoundException(clazz);
  73. }
  74. else if(obj instanceof JARClassLoader)
  75. {
  76. JARClassLoader classLoader = (JARClassLoader)obj;
  77. return classLoader._loadClass(clazz,resolveIt);
  78. }
  79. // if it's not in the class hash, and not marked as
  80. // non-existent, try loading it from the CLASSPATH
  81. try
  82. {
  83. Class cls;
  84. /* Defer to whoever loaded us (such as JShell,
  85. * Echidna, etc) */
  86. ClassLoader parentLoader = getClass().getClassLoader();
  87. if (parentLoader != null)
  88. cls = parentLoader.loadClass(clazz);
  89. else
  90. cls = findSystemClass(clazz);
  91. return cls;
  92. }
  93. catch(ClassNotFoundException cnf)
  94. {
  95. // remember that this class doesn't exist for
  96. // future reference
  97. classHash.put(clazz,NO_CLASS);
  98. throw cnf;
  99. }
  100. } //}}}
  101. //{{{ getResourceAsStream() method
  102. public InputStream getResourceAsStream(String name)
  103. {
  104. if(jar == null)
  105. return null;
  106. try
  107. {
  108. ZipFile zipFile = jar.getZipFile();
  109. ZipEntry entry = zipFile.getEntry(name);
  110. if(entry == null)
  111. return getSystemResourceAsStream(name);
  112. else
  113. return zipFile.getInputStream(entry);
  114. }
  115. catch(IOException io)
  116. {
  117. Log.log(Log.ERROR,this,io);
  118. return null;
  119. }
  120. } //}}}
  121. //{{{ getResource() method
  122. public URL getResource(String name)
  123. {
  124. if(jar == null)
  125. return null;
  126. try
  127. {
  128. ZipFile zipFile = jar.getZipFile();
  129. ZipEntry entry = zipFile.getEntry(name);
  130. if(entry == null)
  131. return getSystemResource(name);
  132. else
  133. return new URL(getResourceAsPath(name));
  134. }
  135. catch(IOException io)
  136. {
  137. Log.log(Log.ERROR,this,io);
  138. return null;
  139. }
  140. } //}}}
  141. //{{{ getResourceAsPath() method
  142. public String getResourceAsPath(String name)
  143. {
  144. if(jar == null)
  145. return null;
  146. if(!name.startsWith("/"))
  147. name = "/" + name;
  148. return "jeditresource:/" + MiscUtilities.getFileName(
  149. jar.getPath()) + "!" + name;
  150. } //}}}
  151. //{{{ getZipFile() method
  152. /**
  153. * @deprecated Call <code>PluginJAR.getZipFile()</code> instead.
  154. */
  155. public ZipFile getZipFile()
  156. {
  157. try
  158. {
  159. return jar.getZipFile();
  160. }
  161. catch(IOException io)
  162. {
  163. Log.log(Log.ERROR,this,io);
  164. return null;
  165. }
  166. } //}}}
  167. //{{{ dump() method
  168. /**
  169. * For debugging.
  170. */
  171. public static void dump()
  172. {
  173. Log.log(Log.DEBUG,JARClassLoader.class,
  174. "Total instances created: " + INDEX);
  175. Log.log(Log.DEBUG,JARClassLoader.class,
  176. "Live instances: " + live);
  177. synchronized(classHash)
  178. {
  179. Iterator entries = classHash.entrySet().iterator();
  180. while(entries.hasNext())
  181. {
  182. Map.Entry entry = (Map.Entry)entries.next();
  183. if(entry.getValue() != NO_CLASS)
  184. {
  185. Log.log(Log.DEBUG,JARClassLoader.class,
  186. entry.getKey() + " ==> "
  187. + entry.getValue());
  188. }
  189. }
  190. }
  191. } //}}}
  192. //{{{ toString() method
  193. public String toString()
  194. {
  195. if(jar == null)
  196. return "<anonymous>(" + id + ")";
  197. else
  198. return jar.getPath() + " (" + id + ")";
  199. } //}}}
  200. //{{{ findResources() method
  201. protected Enumeration findResources(String name) throws IOException
  202. {
  203. class SingleElementEnumeration implements Enumeration
  204. {
  205. private Object element;
  206. public SingleElementEnumeration(Object element)
  207. {
  208. this.element = element;
  209. }
  210. public boolean hasMoreElements()
  211. {
  212. return (element != null);
  213. }
  214. public Object nextElement()
  215. {
  216. if(element != null)
  217. {
  218. Object retval = element;
  219. element = null;
  220. return retval;
  221. }
  222. else
  223. throw new NoSuchElementException();
  224. }
  225. }
  226. URL resource = getResource(name);
  227. return new SingleElementEnumeration(resource);
  228. } //}}}
  229. //{{{ finalize() method
  230. protected void finalize()
  231. {
  232. live--;
  233. } //}}}
  234. //{{{ Package-private members
  235. //{{{ JARClassLoader constructor
  236. /**
  237. * @since jEdit 4.2pre1
  238. */
  239. JARClassLoader(PluginJAR jar)
  240. {
  241. this();
  242. this.jar = jar;
  243. } //}}}
  244. //{{{ activate() method
  245. void activate()
  246. {
  247. String[] classes = jar.getClasses();
  248. if(classes != null)
  249. {
  250. for(int i = 0; i < classes.length; i++)
  251. {
  252. classHash.put(classes[i],this);
  253. }
  254. }
  255. } //}}}
  256. //{{{ deactivate() method
  257. void deactivate()
  258. {
  259. String[] classes = jar.getClasses();
  260. if(classes == null)
  261. return;
  262. for(int i = 0; i < classes.length; i++)
  263. {
  264. Object loader = classHash.get(classes[i]);
  265. if(loader == this)
  266. classHash.remove(classes[i]);
  267. else
  268. /* two plugins provide same class! */;
  269. }
  270. } //}}}
  271. //}}}
  272. //{{{ Private members
  273. // used to mark non-existent classes in class hash
  274. private static final Object NO_CLASS = new Object();
  275. private static int INDEX;
  276. private static int live;
  277. private static Hashtable classHash = new Hashtable();
  278. private int id;
  279. private PluginJAR jar;
  280. //{{{ _loadClass() method
  281. /**
  282. * Load class from this JAR only.
  283. */
  284. private synchronized Class _loadClass(String clazz, boolean resolveIt)
  285. throws ClassNotFoundException
  286. {
  287. jar.activatePlugin();
  288. synchronized(this)
  289. {
  290. Class cls = findLoadedClass(clazz);
  291. if(cls != null)
  292. {
  293. if(resolveIt)
  294. resolveClass(cls);
  295. return cls;
  296. }
  297. String name = MiscUtilities.classToFile(clazz);
  298. try
  299. {
  300. definePackage(clazz);
  301. ZipFile zipFile = jar.getZipFile();
  302. ZipEntry entry = zipFile.getEntry(name);
  303. if(entry == null)
  304. throw new ClassNotFoundException(clazz);
  305. InputStream in = zipFile.getInputStream(entry);
  306. int len = (int)entry.getSize();
  307. byte[] data = new byte[len];
  308. int success = 0;
  309. int offset = 0;
  310. while(success < len)
  311. {
  312. len -= success;
  313. offset += success;
  314. success = in.read(data,offset,len);
  315. if(success == -1)
  316. {
  317. Log.log(Log.ERROR,this,"Failed to load class "
  318. + clazz + " from " + zipFile.getName());
  319. throw new ClassNotFoundException(clazz);
  320. }
  321. }
  322. cls = defineClass(clazz,data,0,data.length);
  323. if(resolveIt)
  324. resolveClass(cls);
  325. return cls;
  326. }
  327. catch(IOException io)
  328. {
  329. Log.log(Log.ERROR,this,io);
  330. throw new ClassNotFoundException(clazz);
  331. }
  332. }
  333. } //}}}
  334. //{{{ definePackage(clazz) method
  335. private void definePackage(String clazz) throws IOException
  336. {
  337. int idx = clazz.lastIndexOf('.');
  338. if (idx != -1) {
  339. String name = clazz.substring(0, idx);
  340. if (getPackage(name) == null) definePackage(name, new JarFile(jar.getFile()).getManifest());
  341. }
  342. } //}}}
  343. //{{{ getMfValue() method
  344. private String getMfValue(Attributes sectionAttrs, Attributes mainAttrs, Attributes.Name name)
  345. {
  346. String value=null;
  347. if (sectionAttrs != null)
  348. value = sectionAttrs.getValue(name);
  349. else if (mainAttrs != null) {
  350. value = mainAttrs.getValue(name);
  351. }
  352. return value;
  353. }
  354. //}}}
  355. //{{{ definePackage(packageName, manifest) method
  356. private void definePackage(String name, Manifest mf)
  357. {
  358. if (mf==null)
  359. {
  360. definePackage(name, null, null, null, null, null,
  361. null, null);
  362. return;
  363. }
  364. Attributes sa = mf.getAttributes(name.replace('.', '/') + "/");
  365. Attributes ma = mf.getMainAttributes();
  366. URL sealBase = null;
  367. if (Boolean.valueOf(getMfValue(sa, ma, Name.SEALED)).booleanValue())
  368. {
  369. try
  370. {
  371. sealBase = jar.getFile().toURL();
  372. } catch (MalformedURLException e) { }
  373. }
  374. Package pkg=definePackage(
  375. name,
  376. getMfValue(sa, ma, Name.SPECIFICATION_TITLE),
  377. getMfValue(sa, ma, Name.SPECIFICATION_VERSION),
  378. getMfValue(sa, ma, Name.SPECIFICATION_VENDOR),
  379. getMfValue(sa, ma, Name.IMPLEMENTATION_TITLE),
  380. getMfValue(sa, ma, Name.IMPLEMENTATION_VERSION),
  381. getMfValue(sa, ma, Name.IMPLEMENTATION_VENDOR),
  382. sealBase);
  383. } //}}}
  384. //}}}
  385. }