PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/JARClassLoader.java

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