PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

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

#
Java | 652 lines | 472 code | 81 blank | 99 comment | 87 complexity | 0e551555496993fefd7b3af80ebe7a45 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, 2000, 2001, 2002 Slava Pestov
  7. * Portions copyright (C) 1999 mike dillon
  8. * Portions copyright (C) 2002 Marco Hunsicker
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, write to the Free Software
  22. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. */
  24. package org.gjt.sp.jedit;
  25. //{{{ Imports
  26. import java.io.*;
  27. import java.lang.reflect.Modifier;
  28. import java.net.*;
  29. import java.util.*;
  30. import java.util.jar.*;
  31. import java.util.zip.*;
  32. import org.gjt.sp.jedit.gui.DockableWindowManager;
  33. import org.gjt.sp.util.Log;
  34. //}}}
  35. /**
  36. * A class loader implementation that loads classes from JAR files.
  37. * @author Slava Pestov
  38. * @version $Id: JARClassLoader.java 4329 2002-08-28 20:51:29Z spestov $
  39. */
  40. public class JARClassLoader extends ClassLoader
  41. {
  42. //{{{ JARClassLoader constructor
  43. /**
  44. * This constructor creates a class loader for loading classes from all
  45. * plugins. For example BeanShell uses one of these so that scripts can
  46. * use plugin classes.
  47. */
  48. public JARClassLoader()
  49. {
  50. } //}}}
  51. //{{{ JARClassLoader constructor
  52. public JARClassLoader(String path)
  53. throws IOException
  54. {
  55. zipFile = new JarFile(path);
  56. definePackages();
  57. jar = new EditPlugin.JAR(path,this);
  58. Enumeration entires = zipFile.entries();
  59. while(entires.hasMoreElements())
  60. {
  61. ZipEntry entry = (ZipEntry)entires.nextElement();
  62. String name = entry.getName();
  63. String lname = name.toLowerCase();
  64. if(lname.equals("actions.xml"))
  65. {
  66. jEdit.loadActions(
  67. path + "!actions.xml",
  68. new BufferedReader(new InputStreamReader(
  69. zipFile.getInputStream(entry))),
  70. jar.getActions());
  71. }
  72. if(lname.equals("dockables.xml"))
  73. {
  74. DockableWindowManager.loadDockableWindows(
  75. path + "!dockables.xml",
  76. new BufferedReader(new InputStreamReader(
  77. zipFile.getInputStream(entry))),
  78. jar.getActions());
  79. }
  80. else if(lname.endsWith(".props"))
  81. jEdit.loadProps(zipFile.getInputStream(entry),true);
  82. else if(name.endsWith(".class"))
  83. {
  84. classHash.put(MiscUtilities.fileToClass(name),this);
  85. if(name.endsWith("Plugin.class"))
  86. pluginClasses.addElement(name);
  87. }
  88. }
  89. jEdit.addPluginJAR(jar);
  90. } //}}}
  91. //{{{ loadClass() method
  92. /**
  93. * @exception ClassNotFoundException if the class could not be found
  94. */
  95. public Class loadClass(String clazz, boolean resolveIt)
  96. throws ClassNotFoundException
  97. {
  98. // see what JARClassLoader this class is in
  99. Object obj = classHash.get(clazz);
  100. if(obj == NO_CLASS)
  101. {
  102. // we remember which classes we don't exist
  103. // because BeanShell tries loading all possible
  104. // <imported prefix>.<class name> combinations
  105. throw new ClassNotFoundException(clazz);
  106. }
  107. else if(obj instanceof ClassLoader)
  108. {
  109. JARClassLoader classLoader = (JARClassLoader)obj;
  110. return classLoader._loadClass(clazz,resolveIt);
  111. }
  112. // if it's not in the class hash, and not marked as
  113. // non-existent, try loading it from the CLASSPATH
  114. try
  115. {
  116. Class cls;
  117. /* Defer to whoever loaded us (such as JShell,
  118. * Echidna, etc) */
  119. ClassLoader parentLoader = getClass().getClassLoader();
  120. if (parentLoader != null)
  121. cls = parentLoader.loadClass(clazz);
  122. else
  123. cls = findSystemClass(clazz);
  124. return cls;
  125. }
  126. catch(ClassNotFoundException cnf)
  127. {
  128. // remember that this class doesn't exist for
  129. // future reference
  130. classHash.put(clazz,NO_CLASS);
  131. throw cnf;
  132. }
  133. } //}}}
  134. //{{{ getResourceAsStream() method
  135. public InputStream getResourceAsStream(String name)
  136. {
  137. if(zipFile == null)
  138. return null;
  139. try
  140. {
  141. ZipEntry entry = zipFile.getEntry(name);
  142. if(entry == null)
  143. return getSystemResourceAsStream(name);
  144. else
  145. return zipFile.getInputStream(entry);
  146. }
  147. catch(IOException io)
  148. {
  149. Log.log(Log.ERROR,this,io);
  150. return null;
  151. }
  152. } //}}}
  153. //{{{ getResource() method
  154. public URL getResource(String name)
  155. {
  156. if(zipFile == null)
  157. return null;
  158. ZipEntry entry = zipFile.getEntry(name);
  159. if(entry == null)
  160. return getSystemResource(name);
  161. try
  162. {
  163. return new URL(getResourceAsPath(name));
  164. }
  165. catch(MalformedURLException mu)
  166. {
  167. Log.log(Log.ERROR,this,mu);
  168. return null;
  169. }
  170. } //}}}
  171. //{{{ getResourceAsPath() method
  172. public String getResourceAsPath(String name)
  173. {
  174. if(zipFile == null)
  175. return null;
  176. if(!name.startsWith("/"))
  177. name = "/" + name;
  178. return "jeditresource:/" + MiscUtilities.getFileName(
  179. jar.getPath()) + "!" + name;
  180. } //}}}
  181. //{{{ closeZipFile() method
  182. /**
  183. * Closes the ZIP file. This plugin will no longer be usable
  184. * after this.
  185. * @since jEdit 2.6pre1
  186. */
  187. public void closeZipFile()
  188. {
  189. if(zipFile == null)
  190. return;
  191. try
  192. {
  193. zipFile.close();
  194. }
  195. catch(IOException io)
  196. {
  197. Log.log(Log.ERROR,this,io);
  198. }
  199. zipFile = null;
  200. } //}}}
  201. //{{{ getZipFile() method
  202. /**
  203. * Returns the ZIP file associated with this class loader.
  204. * @since jEdit 3.0final
  205. */
  206. public ZipFile getZipFile()
  207. {
  208. return zipFile;
  209. } //}}}
  210. //{{{ startAllPlugins() method
  211. void startAllPlugins()
  212. {
  213. for(int i = 0; i < pluginClasses.size(); i++)
  214. {
  215. String name = (String)pluginClasses.elementAt(i);
  216. name = MiscUtilities.fileToClass(name);
  217. try
  218. {
  219. loadPluginClass(name);
  220. }
  221. catch(Throwable t)
  222. {
  223. Log.log(Log.ERROR,this,"Error while starting plugin " + name);
  224. Log.log(Log.ERROR,this,t);
  225. jar.addPlugin(new EditPlugin.Broken(name));
  226. String[] args = { t.toString() };
  227. jEdit.pluginError(jar.getPath(),
  228. "plugin-error.start-error",args);
  229. }
  230. }
  231. } //}}}
  232. //{{{ Private members
  233. // used to mark non-existent classes in class hash
  234. private static final Object NO_CLASS = new Object();
  235. private static Hashtable classHash = new Hashtable();
  236. private EditPlugin.JAR jar;
  237. private Vector pluginClasses = new Vector();
  238. private JarFile zipFile;
  239. //{{{ loadPluginClass() method
  240. private void loadPluginClass(String name)
  241. throws Exception
  242. {
  243. // Check if a plugin with the same name is already loaded
  244. EditPlugin[] plugins = jEdit.getPlugins();
  245. for(int i = 0; i < plugins.length; i++)
  246. {
  247. if(plugins[i].getClass().getName().equals(name))
  248. {
  249. jEdit.pluginError(jar.getPath(),
  250. "plugin-error.already-loaded",null);
  251. return;
  252. }
  253. }
  254. /* This is a bit silly... but WheelMouse seems to be
  255. * unmaintained so the best solution is to add a hack here.
  256. */
  257. if(name.equals("WheelMousePlugin")
  258. && OperatingSystem.hasJava14())
  259. {
  260. jar.addPlugin(new EditPlugin.Broken(name));
  261. jEdit.pluginError(jar.getPath(),"plugin-error.obsolete",null);
  262. return;
  263. }
  264. // Check dependencies
  265. if(!checkDependencies(name))
  266. {
  267. jar.addPlugin(new EditPlugin.Broken(name));
  268. return;
  269. }
  270. // JDK 1.1.8 throws a GPF when we do an isAssignableFrom()
  271. // on an unresolved class
  272. Class clazz = loadClass(name,true);
  273. int modifiers = clazz.getModifiers();
  274. if(!Modifier.isInterface(modifiers)
  275. && !Modifier.isAbstract(modifiers)
  276. && EditPlugin.class.isAssignableFrom(clazz))
  277. {
  278. String label = jEdit.getProperty("plugin."
  279. + name + ".name");
  280. String version = jEdit.getProperty("plugin."
  281. + name + ".version");
  282. if(version == null)
  283. {
  284. Log.log(Log.ERROR,this,"Plugin " +
  285. name + " needs"
  286. + " 'name' and 'version' properties.");
  287. jar.addPlugin(new EditPlugin.Broken(name));
  288. return;
  289. }
  290. jar.getActions().setLabel(jEdit.getProperty(
  291. "action-set.plugin",
  292. new String[] { label }));
  293. Log.log(Log.NOTICE,this,"Starting plugin " + label
  294. + " (version " + version + ")");
  295. jar.addPlugin((EditPlugin)clazz.newInstance());
  296. }
  297. } //}}}
  298. //{{{ checkDependencies() method
  299. private boolean checkDependencies(String name)
  300. {
  301. int i = 0;
  302. String dep;
  303. while((dep = jEdit.getProperty("plugin." + name + ".depend." + i++)) != null)
  304. {
  305. int index = dep.indexOf(' ');
  306. if(index == -1)
  307. {
  308. Log.log(Log.ERROR,this,name + " has an invalid"
  309. + " dependency: " + dep);
  310. return false;
  311. }
  312. String what = dep.substring(0,index);
  313. String arg = dep.substring(index + 1);
  314. if(what.equals("jdk"))
  315. {
  316. if(MiscUtilities.compareStrings(
  317. System.getProperty("java.version"),
  318. arg,false) < 0)
  319. {
  320. String[] args = { arg,
  321. System.getProperty("java.version") };
  322. jEdit.pluginError(jar.getPath(),"plugin-error.dep-jdk",args);
  323. return false;
  324. }
  325. }
  326. else if(what.equals("jedit"))
  327. {
  328. if(arg.length() != 11)
  329. {
  330. Log.log(Log.ERROR,this,"Invalid jEdit version"
  331. + " number: " + arg);
  332. return false;
  333. }
  334. if(MiscUtilities.compareStrings(
  335. jEdit.getBuild(),arg,false) < 0)
  336. {
  337. String needs = MiscUtilities.buildToVersion(arg);
  338. String[] args = { needs,
  339. jEdit.getVersion() };
  340. jEdit.pluginError(jar.getPath(),
  341. "plugin-error.dep-jedit",args);
  342. return false;
  343. }
  344. }
  345. else if(what.equals("plugin"))
  346. {
  347. int index2 = arg.indexOf(' ');
  348. if(index2 == -1)
  349. {
  350. Log.log(Log.ERROR,this,name
  351. + " has an invalid dependency: "
  352. + dep + " (version is missing)");
  353. return false;
  354. }
  355. String plugin = arg.substring(0,index2);
  356. String needVersion = arg.substring(index2 + 1);
  357. String currVersion = jEdit.getProperty("plugin."
  358. + plugin + ".version");
  359. if(currVersion == null)
  360. {
  361. String[] args = { needVersion, plugin };
  362. jEdit.pluginError(jar.getPath(),
  363. "plugin-error.dep-plugin.no-version",
  364. args);
  365. return false;
  366. }
  367. if(MiscUtilities.compareStrings(currVersion,
  368. needVersion,false) < 0)
  369. {
  370. String[] args = { needVersion, plugin, currVersion };
  371. jEdit.pluginError(jar.getPath(),
  372. "plugin-error.dep-plugin",args);
  373. return false;
  374. }
  375. if(jEdit.getPlugin(plugin) instanceof EditPlugin.Broken)
  376. {
  377. String[] args = { plugin };
  378. jEdit.pluginError(jar.getPath(),
  379. "plugin-error.dep-plugin.broken",args);
  380. return false;
  381. }
  382. }
  383. else if(what.equals("class"))
  384. {
  385. try
  386. {
  387. loadClass(arg,false);
  388. }
  389. catch(Exception e)
  390. {
  391. String[] args = { arg };
  392. jEdit.pluginError(jar.getPath(),
  393. "plugin-error.dep-class",args);
  394. return false;
  395. }
  396. }
  397. else
  398. {
  399. Log.log(Log.ERROR,this,name + " has unknown"
  400. + " dependency: " + dep);
  401. return false;
  402. }
  403. }
  404. return true;
  405. } //}}}
  406. //{{{ _loadClass() method
  407. /**
  408. * Load class from this JAR only.
  409. */
  410. private Class _loadClass(String clazz, boolean resolveIt)
  411. throws ClassNotFoundException
  412. {
  413. Class cls = findLoadedClass(clazz);
  414. if(cls != null)
  415. {
  416. if(resolveIt)
  417. resolveClass(cls);
  418. return cls;
  419. }
  420. String name = MiscUtilities.classToFile(clazz);
  421. try
  422. {
  423. ZipEntry entry = zipFile.getEntry(name);
  424. if(entry == null)
  425. throw new ClassNotFoundException(clazz);
  426. InputStream in = zipFile.getInputStream(entry);
  427. int len = (int)entry.getSize();
  428. byte[] data = new byte[len];
  429. int success = 0;
  430. int offset = 0;
  431. while(success < len)
  432. {
  433. len -= success;
  434. offset += success;
  435. success = in.read(data,offset,len);
  436. if(success == -1)
  437. {
  438. Log.log(Log.ERROR,this,"Failed to load class "
  439. + clazz + " from " + zipFile.getName());
  440. throw new ClassNotFoundException(clazz);
  441. }
  442. }
  443. cls = defineClass(clazz,data,0,data.length);
  444. if(resolveIt)
  445. resolveClass(cls);
  446. return cls;
  447. }
  448. catch(IOException io)
  449. {
  450. Log.log(Log.ERROR,this,io);
  451. throw new ClassNotFoundException(clazz);
  452. }
  453. } //}}}
  454. //{{{ definePackages() method
  455. /**
  456. * Defines all packages found in the given Java archive file. The
  457. * attributes contained in the specified Manifest will be used to obtain
  458. * package version and sealing information.
  459. */
  460. private void definePackages()
  461. {
  462. try
  463. {
  464. Manifest manifest = zipFile.getManifest();
  465. if(manifest != null)
  466. {
  467. Map entries = manifest.getEntries();
  468. Iterator i = entries.keySet().iterator();
  469. while(i.hasNext())
  470. {
  471. String path = (String)i.next();
  472. if(!path.endsWith(".class"))
  473. {
  474. String name = path.replace('/', '.');
  475. if(name.endsWith("."))
  476. name = name.substring(0, name.length() - 1);
  477. // code url not implemented
  478. definePackage(path,name,manifest,null);
  479. }
  480. }
  481. }
  482. }
  483. catch (Exception ex)
  484. {
  485. // should never happen, not severe anyway
  486. Log.log(Log.ERROR, this,"Error extracting manifest info "
  487. + "for file " + zipFile);
  488. Log.log(Log.ERROR, this, ex);
  489. }
  490. } //}}}
  491. //{{{ definePackage() method
  492. /**
  493. * Defines a new package by name in this ClassLoader. The attributes
  494. * contained in the specified Manifest will be used to obtain package
  495. * version and sealing information. For sealed packages, the additional
  496. * URL specifies the code source URL from which the package was loaded.
  497. */
  498. private Package definePackage(String path, String name, Manifest man,
  499. URL url) throws IllegalArgumentException
  500. {
  501. String specTitle = null;
  502. String specVersion = null;
  503. String specVendor = null;
  504. String implTitle = null;
  505. String implVersion = null;
  506. String implVendor = null;
  507. String sealed = null;
  508. URL sealBase = null;
  509. Attributes attr = man.getAttributes(path);
  510. if(attr != null)
  511. {
  512. specTitle = attr.getValue(
  513. Attributes.Name.SPECIFICATION_TITLE);
  514. specVersion = attr.getValue(
  515. Attributes.Name.SPECIFICATION_VERSION);
  516. specVendor = attr.getValue(
  517. Attributes.Name.SPECIFICATION_VENDOR);
  518. implTitle = attr.getValue(
  519. Attributes.Name.IMPLEMENTATION_TITLE);
  520. implVersion = attr.getValue(
  521. Attributes.Name.IMPLEMENTATION_VERSION);
  522. implVendor = attr.getValue(
  523. Attributes.Name.IMPLEMENTATION_VENDOR);
  524. sealed = attr.getValue(Attributes.Name.SEALED);
  525. }
  526. attr = man.getMainAttributes();
  527. if (attr != null)
  528. {
  529. if (specTitle == null)
  530. {
  531. specTitle = attr.getValue(
  532. Attributes.Name.SPECIFICATION_TITLE);
  533. }
  534. if (specVersion == null)
  535. {
  536. specVersion = attr.getValue(
  537. Attributes.Name.SPECIFICATION_VERSION);
  538. }
  539. if (specVendor == null)
  540. {
  541. specVendor = attr.getValue(
  542. Attributes.Name.SPECIFICATION_VENDOR);
  543. }
  544. if (implTitle == null)
  545. {
  546. implTitle = attr.getValue(
  547. Attributes.Name.IMPLEMENTATION_TITLE);
  548. }
  549. if (implVersion == null)
  550. {
  551. implVersion = attr.getValue(
  552. Attributes.Name.IMPLEMENTATION_VERSION);
  553. }
  554. if (implVendor == null)
  555. {
  556. implVendor = attr.getValue(
  557. Attributes.Name.IMPLEMENTATION_VENDOR);
  558. }
  559. if (sealed == null)
  560. {
  561. sealed = attr.getValue(Attributes.Name.SEALED);
  562. }
  563. }
  564. //if("true".equalsIgnoreCase(sealed))
  565. // sealBase = url;
  566. return super.definePackage(name, specTitle, specVersion, specVendor,
  567. implTitle, implVersion, implVendor,
  568. sealBase);
  569. } //}}}
  570. //}}}
  571. }