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

# · Java · 477 lines · 332 code · 57 blank · 88 comment · 58 complexity · 655f023620e208fc65ec68ce4c0051ab MD5 · raw file

  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 6459 2006-07-30 00:54:17Z vanza $
  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. this(true);
  55. }
  56. /**
  57. * Creates a class loader that will optionally delegate the
  58. * finding of classes to the parent class loader by default.
  59. *
  60. * @since jEdit 4.3pre6
  61. */
  62. public JARClassLoader(boolean delegateFirst) {
  63. this.delegateFirst = delegateFirst;
  64. // for debugging
  65. id = INDEX++;
  66. live++;
  67. } //}}}
  68. //{{{ loadClass() method
  69. /**
  70. * @exception ClassNotFoundException if the class could not be found
  71. */
  72. public Class loadClass(String clazz, boolean resolveIt)
  73. throws ClassNotFoundException
  74. {
  75. ClassNotFoundException pending = null;
  76. if (delegateFirst)
  77. {
  78. try
  79. {
  80. return loadFromParent(clazz);
  81. }
  82. catch (ClassNotFoundException cnf)
  83. {
  84. // keep going if class was not found.
  85. pending = cnf;
  86. }
  87. }
  88. Object obj = classHash.get(clazz);
  89. if(obj == NO_CLASS)
  90. {
  91. // we remember which classes we don't exist
  92. // because BeanShell tries loading all possible
  93. // <imported prefix>.<class name> combinations
  94. throw new ClassNotFoundException(clazz);
  95. }
  96. else if(obj instanceof JARClassLoader)
  97. {
  98. JARClassLoader classLoader = (JARClassLoader)obj;
  99. try
  100. {
  101. return classLoader._loadClass(clazz,resolveIt);
  102. } catch (ClassNotFoundException cnf2)
  103. {
  104. classHash.put(clazz,NO_CLASS);
  105. throw cnf2;
  106. }
  107. }
  108. else if (delegateFirst)
  109. {
  110. // if delegating, reaching this statement means
  111. // the class was really not found. Otherwise
  112. // we'll try loading from the parent class loader.
  113. throw pending;
  114. }
  115. return loadFromParent(clazz);
  116. } //}}}
  117. //{{{ getResourceAsStream() method
  118. public InputStream getResourceAsStream(String name)
  119. {
  120. if(jar == null)
  121. return null;
  122. try
  123. {
  124. ZipFile zipFile = jar.getZipFile();
  125. ZipEntry entry = zipFile.getEntry(name);
  126. if(entry == null)
  127. return getSystemResourceAsStream(name);
  128. else
  129. return zipFile.getInputStream(entry);
  130. }
  131. catch(IOException io)
  132. {
  133. Log.log(Log.ERROR,this,io);
  134. return null;
  135. }
  136. } //}}}
  137. //{{{ getResource() method
  138. public URL getResource(String name)
  139. {
  140. if(jar == null)
  141. return null;
  142. try
  143. {
  144. ZipFile zipFile = jar.getZipFile();
  145. ZipEntry entry = zipFile.getEntry(name);
  146. if(entry == null)
  147. return getSystemResource(name);
  148. else
  149. return new URL(getResourceAsPath(name));
  150. }
  151. catch(IOException io)
  152. {
  153. Log.log(Log.ERROR,this,io);
  154. return null;
  155. }
  156. } //}}}
  157. //{{{ getResourceAsPath() method
  158. public String getResourceAsPath(String name)
  159. {
  160. if(jar == null)
  161. return null;
  162. if(!name.startsWith("/"))
  163. name = "/" + name;
  164. return "jeditresource:/" + MiscUtilities.getFileName(
  165. jar.getPath()) + "!" + name;
  166. } //}}}
  167. //{{{ getZipFile() method
  168. /**
  169. * @deprecated Call <code>PluginJAR.getZipFile()</code> instead.
  170. */
  171. public ZipFile getZipFile()
  172. {
  173. try
  174. {
  175. return jar.getZipFile();
  176. }
  177. catch(IOException io)
  178. {
  179. Log.log(Log.ERROR,this,io);
  180. return null;
  181. }
  182. } //}}}
  183. //{{{ dump() method
  184. /**
  185. * For debugging.
  186. */
  187. public static void dump()
  188. {
  189. Log.log(Log.DEBUG,JARClassLoader.class,
  190. "Total instances created: " + INDEX);
  191. Log.log(Log.DEBUG,JARClassLoader.class,
  192. "Live instances: " + live);
  193. synchronized(classHash)
  194. {
  195. Iterator entries = classHash.entrySet().iterator();
  196. while(entries.hasNext())
  197. {
  198. Map.Entry entry = (Map.Entry)entries.next();
  199. if(entry.getValue() != NO_CLASS)
  200. {
  201. Log.log(Log.DEBUG,JARClassLoader.class,
  202. entry.getKey() + " ==> "
  203. + entry.getValue());
  204. }
  205. }
  206. }
  207. } //}}}
  208. //{{{ toString() method
  209. public String toString()
  210. {
  211. if(jar == null)
  212. return "<anonymous>(" + id + ")";
  213. else
  214. return jar.getPath() + " (" + id + ")";
  215. } //}}}
  216. //{{{ findResources() method
  217. protected Enumeration findResources(String name) throws IOException
  218. {
  219. class SingleElementEnumeration implements Enumeration
  220. {
  221. private Object element;
  222. public SingleElementEnumeration(Object element)
  223. {
  224. this.element = element;
  225. }
  226. public boolean hasMoreElements()
  227. {
  228. return (element != null);
  229. }
  230. public Object nextElement()
  231. {
  232. if(element != null)
  233. {
  234. Object retval = element;
  235. element = null;
  236. return retval;
  237. }
  238. else
  239. throw new NoSuchElementException();
  240. }
  241. }
  242. URL resource = getResource(name);
  243. return new SingleElementEnumeration(resource);
  244. } //}}}
  245. //{{{ finalize() method
  246. protected void finalize()
  247. {
  248. live--;
  249. } //}}}
  250. //{{{ Package-private members
  251. //{{{ JARClassLoader constructor
  252. /**
  253. * @since jEdit 4.2pre1
  254. */
  255. JARClassLoader(PluginJAR jar)
  256. {
  257. this();
  258. this.jar = jar;
  259. } //}}}
  260. //{{{ activate() method
  261. void activate()
  262. {
  263. if (jar.getPlugin() != null)
  264. {
  265. String _delegate = jEdit.getProperty(
  266. "plugin." + jar.getPlugin().getClassName() + ".class_loader_delegate");
  267. delegateFirst = (_delegate == null || "true".equals(_delegate));
  268. }
  269. String[] classes = jar.getClasses();
  270. if(classes != null)
  271. {
  272. for(int i = 0; i < classes.length; i++)
  273. {
  274. classHash.put(classes[i],this);
  275. }
  276. }
  277. } //}}}
  278. //{{{ deactivate() method
  279. void deactivate()
  280. {
  281. String[] classes = jar.getClasses();
  282. if(classes == null)
  283. return;
  284. for(int i = 0; i < classes.length; i++)
  285. {
  286. Object loader = classHash.get(classes[i]);
  287. if(loader == this)
  288. classHash.remove(classes[i]);
  289. else
  290. /* two plugins provide same class! */;
  291. }
  292. } //}}}
  293. //}}}
  294. //{{{ Private members
  295. // used to mark non-existent classes in class hash
  296. private static final Object NO_CLASS = new Object();
  297. private static int INDEX;
  298. private static int live;
  299. private static Hashtable classHash = new Hashtable();
  300. private int id;
  301. private boolean delegateFirst;
  302. private PluginJAR jar;
  303. //{{{ _loadClass() method
  304. /**
  305. * Load class from this JAR only.
  306. */
  307. private synchronized Class _loadClass(String clazz, boolean resolveIt)
  308. throws ClassNotFoundException
  309. {
  310. jar.activatePlugin();
  311. synchronized(this)
  312. {
  313. Class cls = findLoadedClass(clazz);
  314. if(cls != null)
  315. {
  316. if(resolveIt)
  317. resolveClass(cls);
  318. return cls;
  319. }
  320. String name = MiscUtilities.classToFile(clazz);
  321. try
  322. {
  323. definePackage(clazz);
  324. ZipFile zipFile = jar.getZipFile();
  325. ZipEntry entry = zipFile.getEntry(name);
  326. if(entry == null)
  327. throw new ClassNotFoundException(clazz);
  328. InputStream in = zipFile.getInputStream(entry);
  329. int len = (int)entry.getSize();
  330. byte[] data = new byte[len];
  331. int success = 0;
  332. int offset = 0;
  333. while(success < len)
  334. {
  335. len -= success;
  336. offset += success;
  337. success = in.read(data,offset,len);
  338. if(success == -1)
  339. {
  340. Log.log(Log.ERROR,this,"Failed to load class "
  341. + clazz + " from " + zipFile.getName());
  342. throw new ClassNotFoundException(clazz);
  343. }
  344. }
  345. cls = defineClass(clazz,data,0,data.length);
  346. if(resolveIt)
  347. resolveClass(cls);
  348. return cls;
  349. }
  350. catch(IOException io)
  351. {
  352. Log.log(Log.ERROR,this,io);
  353. throw new ClassNotFoundException(clazz);
  354. }
  355. }
  356. } //}}}
  357. //{{{ definePackage(clazz) method
  358. private void definePackage(String clazz) throws IOException
  359. {
  360. int idx = clazz.lastIndexOf('.');
  361. if (idx != -1) {
  362. String name = clazz.substring(0, idx);
  363. if (getPackage(name) == null) definePackage(name, new JarFile(jar.getFile()).getManifest());
  364. }
  365. } //}}}
  366. //{{{ getMfValue() method
  367. private String getMfValue(Attributes sectionAttrs, Attributes mainAttrs, Attributes.Name name)
  368. {
  369. String value=null;
  370. if (sectionAttrs != null)
  371. value = sectionAttrs.getValue(name);
  372. else if (mainAttrs != null) {
  373. value = mainAttrs.getValue(name);
  374. }
  375. return value;
  376. }
  377. //}}}
  378. //{{{ definePackage(packageName, manifest) method
  379. private void definePackage(String name, Manifest mf)
  380. {
  381. if (mf==null)
  382. {
  383. definePackage(name, null, null, null, null, null,
  384. null, null);
  385. return;
  386. }
  387. Attributes sa = mf.getAttributes(name.replace('.', '/') + "/");
  388. Attributes ma = mf.getMainAttributes();
  389. URL sealBase = null;
  390. if (Boolean.valueOf(getMfValue(sa, ma, Name.SEALED)).booleanValue())
  391. {
  392. try
  393. {
  394. sealBase = jar.getFile().toURL();
  395. } catch (MalformedURLException e) { }
  396. }
  397. Package pkg=definePackage(
  398. name,
  399. getMfValue(sa, ma, Name.SPECIFICATION_TITLE),
  400. getMfValue(sa, ma, Name.SPECIFICATION_VERSION),
  401. getMfValue(sa, ma, Name.SPECIFICATION_VENDOR),
  402. getMfValue(sa, ma, Name.IMPLEMENTATION_TITLE),
  403. getMfValue(sa, ma, Name.IMPLEMENTATION_VERSION),
  404. getMfValue(sa, ma, Name.IMPLEMENTATION_VENDOR),
  405. sealBase);
  406. } //}}}
  407. //{{{ loadFromParent() method
  408. private Class loadFromParent(String clazz)
  409. throws ClassNotFoundException
  410. {
  411. Class cls;
  412. ClassLoader parentLoader = getClass().getClassLoader();
  413. if (parentLoader != null)
  414. cls = parentLoader.loadClass(clazz);
  415. else
  416. cls = findSystemClass(clazz);
  417. return cls;
  418. } //}}}
  419. //}}}
  420. }