PageRenderTime 50ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre3/org/gjt/sp/jedit/JARClassLoader.java

#
Java | 474 lines | 358 code | 59 blank | 57 comment | 61 complexity | dad89294791421f75af0c4fd8ecb4cc4 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. * Copyright (C) 1999, 2000, 2001 Slava Pestov
  4. * Portions copyright (C) 1999 mike dillon
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. */
  20. package org.gjt.sp.jedit;
  21. import java.io.*;
  22. import java.lang.reflect.Modifier;
  23. import java.net.*;
  24. import java.util.*;
  25. import java.util.zip.*;
  26. import org.gjt.sp.jedit.gui.DockableWindowManager;
  27. import org.gjt.sp.util.Log;
  28. /**
  29. * A class loader implementation that loads classes from JAR files.
  30. * @author Slava Pestov
  31. * @version $Id: JARClassLoader.java 3928 2001-12-01 05:48:48Z spestov $
  32. */
  33. public class JARClassLoader extends ClassLoader
  34. {
  35. // no-args constructor is for loading classes from all plugins
  36. // eg BeanShell uses one of these so that scripts can use
  37. // plugin classes
  38. public JARClassLoader()
  39. {
  40. }
  41. public JARClassLoader(String path)
  42. throws IOException
  43. {
  44. zipFile = new ZipFile(path);
  45. jar = new EditPlugin.JAR(path,this);
  46. Enumeration entires = zipFile.entries();
  47. while(entires.hasMoreElements())
  48. {
  49. ZipEntry entry = (ZipEntry)entires.nextElement();
  50. String name = entry.getName();
  51. String lname = name.toLowerCase();
  52. if(lname.equals("actions.xml"))
  53. {
  54. jEdit.loadActions(
  55. path + "!actions.xml",
  56. new BufferedReader(new InputStreamReader(
  57. zipFile.getInputStream(entry))),
  58. jar.getActions());
  59. }
  60. if(lname.equals("dockables.xml"))
  61. {
  62. DockableWindowManager.loadDockableWindows(
  63. path + "!dockables.xml",
  64. new BufferedReader(new InputStreamReader(
  65. zipFile.getInputStream(entry))),
  66. jar.getActions());
  67. }
  68. else if(lname.endsWith(".props"))
  69. jEdit.loadProps(zipFile.getInputStream(entry),true);
  70. else if(name.endsWith(".class"))
  71. {
  72. classHash.put(MiscUtilities.fileToClass(name),this);
  73. if(name.endsWith("Plugin.class"))
  74. pluginClasses.addElement(name);
  75. }
  76. }
  77. jEdit.addPluginJAR(jar);
  78. }
  79. /**
  80. * @exception ClassNotFoundException if the class could not be found
  81. */
  82. public Class loadClass(String clazz, boolean resolveIt)
  83. throws ClassNotFoundException
  84. {
  85. // see what JARClassLoader this class is in
  86. Object obj = classHash.get(clazz);
  87. if(obj == NO_CLASS)
  88. {
  89. // we remember which classes we don't exist
  90. // because BeanShell tries loading all possible
  91. // <imported prefix>.<class name> combinations
  92. throw new ClassNotFoundException(clazz);
  93. }
  94. else if(obj instanceof ClassLoader)
  95. {
  96. JARClassLoader classLoader = (JARClassLoader)obj;
  97. return classLoader._loadClass(clazz,resolveIt);
  98. }
  99. // if it's not in the class hash, and not marked as
  100. // non-existent, try loading it from the CLASSPATH
  101. try
  102. {
  103. Class cls;
  104. /* Defer to whoever loaded us (such as JShell,
  105. * Echidna, etc) */
  106. ClassLoader parentLoader = getClass().getClassLoader();
  107. if (parentLoader != null)
  108. cls = parentLoader.loadClass(clazz);
  109. else
  110. cls = findSystemClass(clazz);
  111. return cls;
  112. }
  113. catch(ClassNotFoundException cnf)
  114. {
  115. // remember that this class doesn't exist for
  116. // future reference
  117. classHash.put(clazz,NO_CLASS);
  118. throw cnf;
  119. }
  120. }
  121. public InputStream getResourceAsStream(String name)
  122. {
  123. if(zipFile == null)
  124. return null;
  125. try
  126. {
  127. ZipEntry entry = zipFile.getEntry(name);
  128. if(entry == null)
  129. return getSystemResourceAsStream(name);
  130. else
  131. return zipFile.getInputStream(entry);
  132. }
  133. catch(IOException io)
  134. {
  135. Log.log(Log.ERROR,this,io);
  136. return null;
  137. }
  138. }
  139. public URL getResource(String name)
  140. {
  141. if(zipFile == null)
  142. return null;
  143. ZipEntry entry = zipFile.getEntry(name);
  144. if(entry == null)
  145. return getSystemResource(name);
  146. try
  147. {
  148. return new URL(getResourceAsPath(name));
  149. }
  150. catch(MalformedURLException mu)
  151. {
  152. Log.log(Log.ERROR,this,mu);
  153. return null;
  154. }
  155. }
  156. public String getResourceAsPath(String name)
  157. {
  158. if(zipFile == null)
  159. return null;
  160. if(!name.startsWith("/"))
  161. name = "/" + name;
  162. return "jeditresource:/" + MiscUtilities.getFileName(
  163. jar.getPath()) + "!" + name;
  164. }
  165. /**
  166. * Closes the ZIP file. This plugin will no longer be usable
  167. * after this.
  168. * @since jEdit 2.6pre1
  169. */
  170. public void closeZipFile()
  171. {
  172. if(zipFile == null)
  173. return;
  174. try
  175. {
  176. zipFile.close();
  177. }
  178. catch(IOException io)
  179. {
  180. Log.log(Log.ERROR,this,io);
  181. }
  182. zipFile = null;
  183. }
  184. /**
  185. * Returns the ZIP file associated with this class loader.
  186. * @since jEdit 3.0final
  187. */
  188. public ZipFile getZipFile()
  189. {
  190. return zipFile;
  191. }
  192. // package-private members
  193. void startAllPlugins()
  194. {
  195. for(int i = 0; i < pluginClasses.size(); i++)
  196. {
  197. String name = (String)pluginClasses.elementAt(i);
  198. name = MiscUtilities.fileToClass(name);
  199. try
  200. {
  201. loadPluginClass(name);
  202. }
  203. catch(Throwable t)
  204. {
  205. Log.log(Log.ERROR,this,"Error while starting plugin " + name);
  206. Log.log(Log.ERROR,this,t);
  207. jar.addPlugin(new EditPlugin.Broken(name));
  208. String[] args = { t.toString() };
  209. jEdit.pluginError(jar.getPath(),
  210. "plugin-error.start-error",args);
  211. }
  212. }
  213. }
  214. // private members
  215. // used to mark non-existent classes in class hash
  216. private static final Object NO_CLASS = new Object();
  217. private static Hashtable classHash = new Hashtable();
  218. private EditPlugin.JAR jar;
  219. private Vector pluginClasses = new Vector();
  220. private ZipFile zipFile;
  221. private void loadPluginClass(String name)
  222. throws Exception
  223. {
  224. // Check if a plugin with the same name is already loaded
  225. EditPlugin[] plugins = jEdit.getPlugins();
  226. for(int i = 0; i < plugins.length; i++)
  227. {
  228. if(plugins[i].getClass().getName().equals(name))
  229. {
  230. jEdit.pluginError(jar.getPath(),
  231. "plugin-error.already-loaded",null);
  232. return;
  233. }
  234. }
  235. // Check dependencies
  236. if(!checkDependencies(name))
  237. {
  238. jar.addPlugin(new EditPlugin.Broken(name));
  239. return;
  240. }
  241. // JDK 1.1.8 throws a GPF when we do an isAssignableFrom()
  242. // on an unresolved class
  243. Class clazz = loadClass(name,true);
  244. int modifiers = clazz.getModifiers();
  245. if(!Modifier.isInterface(modifiers)
  246. && !Modifier.isAbstract(modifiers)
  247. && EditPlugin.class.isAssignableFrom(clazz))
  248. {
  249. String label = jEdit.getProperty("plugin."
  250. + name + ".name");
  251. String version = jEdit.getProperty("plugin."
  252. + name + ".version");
  253. if(version == null)
  254. {
  255. Log.log(Log.ERROR,this,"Plugin " +
  256. name + " needs"
  257. + " 'name' and 'version' properties.");
  258. jar.addPlugin(new EditPlugin.Broken(name));
  259. return;
  260. }
  261. jar.getActions().setLabel(jEdit.getProperty(
  262. "action-set.plugin",
  263. new String[] { label }));
  264. Log.log(Log.NOTICE,this,"Starting plugin " + label
  265. + " (version " + version + ")");
  266. jar.addPlugin((EditPlugin)clazz.newInstance());
  267. }
  268. }
  269. private boolean checkDependencies(String name)
  270. {
  271. int i = 0;
  272. String dep;
  273. while((dep = jEdit.getProperty("plugin." + name + ".depend." + i++)) != null)
  274. {
  275. int index = dep.indexOf(' ');
  276. if(index == -1)
  277. {
  278. Log.log(Log.ERROR,this,name + " has an invalid"
  279. + " dependency: " + dep);
  280. return false;
  281. }
  282. String what = dep.substring(0,index);
  283. String arg = dep.substring(index + 1);
  284. if(what.equals("jdk"))
  285. {
  286. if(MiscUtilities.compareStrings(
  287. System.getProperty("java.version"),
  288. arg,false) < 0)
  289. {
  290. String[] args = { arg,
  291. System.getProperty("java.version") };
  292. jEdit.pluginError(jar.getPath(),"plugin-error.dep-jdk",args);
  293. return false;
  294. }
  295. }
  296. else if(what.equals("jedit"))
  297. {
  298. if(MiscUtilities.compareStrings(
  299. jEdit.getBuild(),arg,false) < 0)
  300. {
  301. String needs = MiscUtilities.buildToVersion(arg);
  302. String[] args = { needs,
  303. jEdit.getVersion() };
  304. jEdit.pluginError(jar.getPath(),
  305. "plugin-error.dep-jedit",args);
  306. return false;
  307. }
  308. }
  309. else if(what.equals("plugin"))
  310. {
  311. int index2 = arg.indexOf(' ');
  312. if(index2 == -1)
  313. {
  314. Log.log(Log.ERROR,this,name
  315. + " has an invalid dependency: "
  316. + dep + " (version is missing)");
  317. return false;
  318. }
  319. String plugin = arg.substring(0,index2);
  320. String needVersion = arg.substring(index2 + 1);
  321. String currVersion = jEdit.getProperty("plugin."
  322. + plugin + ".version");
  323. if(currVersion == null)
  324. {
  325. String[] args = { needVersion, plugin };
  326. jEdit.pluginError(jar.getPath(),
  327. "plugin-error.dep-plugin.no-version",
  328. args);
  329. return false;
  330. }
  331. if(MiscUtilities.compareStrings(currVersion,
  332. needVersion,false) < 0)
  333. {
  334. String[] args = { needVersion, plugin, currVersion };
  335. jEdit.pluginError(jar.getPath(),
  336. "plugin-error.dep-plugin",args);
  337. return false;
  338. }
  339. if(jEdit.getPlugin(plugin) instanceof EditPlugin.Broken)
  340. {
  341. String[] args = { plugin };
  342. jEdit.pluginError(jar.getPath(),
  343. "plugin-error.dep-plugin.broken",args);
  344. return false;
  345. }
  346. }
  347. else if(what.equals("class"))
  348. {
  349. try
  350. {
  351. loadClass(arg,false);
  352. }
  353. catch(Exception e)
  354. {
  355. String[] args = { arg };
  356. jEdit.pluginError(jar.getPath(),
  357. "plugin-error.dep-class",args);
  358. return false;
  359. }
  360. }
  361. else
  362. {
  363. Log.log(Log.ERROR,this,name + " has unknown"
  364. + " dependency: " + dep);
  365. return false;
  366. }
  367. }
  368. return true;
  369. }
  370. // Load class from this JAR only.
  371. private Class _loadClass(String clazz, boolean resolveIt)
  372. throws ClassNotFoundException
  373. {
  374. Class cls = findLoadedClass(clazz);
  375. if(cls != null)
  376. {
  377. if(resolveIt)
  378. resolveClass(cls);
  379. return cls;
  380. }
  381. String name = MiscUtilities.classToFile(clazz);
  382. try
  383. {
  384. ZipEntry entry = zipFile.getEntry(name);
  385. if(entry == null)
  386. throw new ClassNotFoundException(clazz);
  387. InputStream in = zipFile.getInputStream(entry);
  388. int len = (int)entry.getSize();
  389. byte[] data = new byte[len];
  390. int success = 0;
  391. int offset = 0;
  392. while(success < len)
  393. {
  394. len -= success;
  395. offset += success;
  396. success = in.read(data,offset,len);
  397. if(success == -1)
  398. {
  399. Log.log(Log.ERROR,this,"Failed to load class "
  400. + clazz + " from " + zipFile.getName());
  401. throw new ClassNotFoundException(clazz);
  402. }
  403. }
  404. cls = defineClass(clazz,data,0,data.length);
  405. if(resolveIt)
  406. resolveClass(cls);
  407. return cls;
  408. }
  409. catch(IOException io)
  410. {
  411. Log.log(Log.ERROR,this,io);
  412. throw new ClassNotFoundException(clazz);
  413. }
  414. }
  415. }