1. import java.io.ByteArrayOutputStream;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4.  
  5. import java.net.MalformedURLException;
  6. import java.net.URL;
  7. import java.net.URLClassLoader;
  8.  
  9. import java.util.ArrayList;
  10. import java.util.HashMap;
  11. import java.util.List;
  12. import java.util.Map;
  13. import java.util.jar.Manifest;
  14. import java.util.logging.Level;
  15. import java.util.logging.Logger;
  16. import java.util.zip.ZipEntry;
  17. import java.util.zip.ZipInputStream;
  18.  
  19.  
  20. /**
  21. * Diese Klasse ermöglicht es auf Klassen und Resourcen in einer JAR und
  22. * in einer JAR, die in der JAR liegt zuzugreifen. Dazu wird NICHT ausgelagert
  23. * und zwischengespeichert. Beim ersten Laden einer Klassen kann es bei
  24. * doppelter Verpackung (zip) zu Verzögerungen kommen.
  25. *
  26. * LICENCE: Diese Klasse darf in nicht kommerziellen Projekten verwendet
  27. * werden. Erweiterungen, Verbesserungen sollten, wenn möglich zu mir zurück
  28. * fliesen, um eine einheitliche Weiterentwicklung zu gewährleisten.
  29. *
  30. * NEUGIER: Wenn jemand diese Klasse verwendet, wäre ich über Rückmeldungen
  31. * sehr dankbar.
  32. *
  33. * @author Clemens Gogolin (Feb. 2008, Braunschweig, Germany)
  34. * @version 0.9b
  35. */
  36. public class PlugInClassLoader extends URLClassLoader {
  37.  
  38. // Liste aller eingebetter JARs (Liste weil geordnet!)
  39. private List<String> nestedJarPath = new ArrayList<String>();
  40. // Relation: eingebetter JARs <-> Manifest
  41. private Map<String, Manifest> manifests = new HashMap<String, Manifest>();
  42.  
  43. // URL des PlugIn-Container-JAR
  44. private URL plugInURL = null;
  45.  
  46. // Index-Map
  47. private Map<String, String> indexMap = new HashMap<String, String>();
  48.  
  49. // Logging
  50. static Logger logger = Logger.getLogger(PlugInClassLoader.class.getName());
  51. // Logging-Switch
  52. static Level level = Level.OFF;
  53.  
  54. public PlugInClassLoader(URL _url) {
  55. super(new URL[] { _url });
  56. this.plugInURL = _url;
  57. logger.setLevel(level);
  58. }
  59.  
  60. public PlugInClassLoader(URL _url, ClassLoader parent) {
  61. super(new URL[] { _url }, parent);
  62. this.plugInURL = _url;
  63. logger.setLevel(level);
  64. }
  65.  
  66. /**
  67.   * Hinzufügen eines einbegetteten JARs und diese JAR wird indiziert.
  68.   * @param _nestedJarPath realtiver Pfad im JAR auf ein JAR (ohne / zu Beginn!)
  69.   */
  70. public void addNestedJarPath(String _nestedJarPath) {
  71. if (super.getResource(_nestedJarPath) != null) {
  72. this.nestedJarPath.add(_nestedJarPath);
  73. Manifest _manifest = null;
  74. try {
  75. _manifest = new Manifest(this.getResourceAsStream("META-INF/MANIFEST.MF"));
  76.  
  77. } catch (IOException e) {
  78. _manifest = new Manifest();
  79. }
  80. this.manifests.put(_nestedJarPath, _manifest);
  81. this.indexNestedJar(_nestedJarPath);
  82. } else
  83. throw new RuntimeException("Nested jar-file not found!");
  84. }
  85.  
  86. /**
  87.   * Entfernt eine eingebette JAR. Dann werden werden die Indexeinträge gelöscht.
  88.   * und nach gesehen ob vielleicht ein nachfolgendes JAR die passende Resource
  89.   * auch beinhaltet.
  90.   * @param _nestedJarPath realtiver Pfad im JAR auf ein JAR (ohne / zu Beginn!)
  91.   */
  92. public void removeNestedJarPath(String _nestedJarPath) {
  93. if (this.nestedJarPath.contains(_nestedJarPath)) {
  94. int _index = this.nestedJarPath.indexOf(_nestedJarPath);
  95. this.nestedJarPath.remove(_index);
  96. this.manifests.remove(_nestedJarPath);
  97. Object[] _keys = this.indexMap.keySet().toArray();
  98. for (int _i = _index, _length = _keys.length; _i < _length; _i++)
  99. if (_nestedJarPath.equals(this.indexMap.get(_keys[_i])))
  100. this.indexMap.remove(_keys[_i]);
  101. for (int _i = 0, size = this.nestedJarPath.size(); _i < size; _i++)
  102. this.indexNestedJar(nestedJarPath.get(_i));
  103. } else
  104. throw new RuntimeException("Nested jar-file not found!");
  105. }
  106.  
  107. /**
  108.   * Routine zum Indizieren (ganz simpel: Dateipfad -> eingebettete JAR).
  109.   * @param _nestedJarPath realtiver Pfad im JAR auf ein JAR (ohne / zu Beginn!)
  110.   */
  111. private void indexNestedJar(String _nestedJarPath) {
  112. String _path = "";
  113. try {
  114. ZipInputStream _zipInputStream = new ZipInputStream(super.getResourceAsStream(_nestedJarPath));
  115. ZipEntry _zipEntry = null;
  116. while ((_zipEntry = _zipInputStream.getNextEntry()) != null) {
  117. _path = _zipEntry.getName();
  118. if (!this.indexMap.containsKey(_path)) {
  119. this.indexMap.put(_path, _nestedJarPath);
  120. logger.log(Level.WARNING,"Index: "+_path + " => " + _nestedJarPath);
  121. }
  122. }
  123. } catch (IOException e) {
  124. logger.log(Level.WARNING, " private void indexNestedJar(String " + _nestedJarPath + " );");
  125. }
  126. }
  127.  
  128. /**
  129.   * Wenn <code>super.findResource(name);</code> nix findet, wird im Index
  130.   * nach gesehen, ob diese Datei bekannt ist.!
  131.   * @param name Rourcenname
  132.   * @return Eine URL (sie hier erzeugt wurde beginnt sie mit "jar:jar:" und kann
  133.   * von Java nicht interpretiert werden!)
  134.   */
  135. public URL findResource(final String name) {
  136. URL _return = super.findResource(name);
  137. if (_return == null) {
  138. String _nestedJarPath = this.indexMap.get(name);
  139. if (_nestedJarPath != null) {
  140. try {
  141. _return = new URL("jar:jar:" + this.plugInURL + "!/" + _nestedJarPath + "!/" + name);
  142. } catch (MalformedURLException e) {
  143. _return = null;
  144. }
  145. }
  146. }
  147. return _return;
  148. }
  149.  
  150. /**
  151.   * Interne Methode zum Finden von Klassen. Dazu wir die Resource als Stream
  152.   * ausfindig gemacht und ne Klasse ganz per Hand erzeugt.
  153.   * @param _name Klassenname
  154.   * @return Die fertige Klasse
  155.   * @throws ClassNotFoundException Doch nicht gefunden
  156.   */
  157. protected Class _findClass(String _name) throws ClassNotFoundException {
  158. String _resourcePath = _name.replaceAll("\\.", "/").concat(".class");
  159. InputStream _byteCodeStream = getResourceAsStream(_resourcePath);
  160. if (_byteCodeStream != null) {
  161. String _nestedJarPath = this.indexMap.get(_resourcePath);
  162. // Package anlegen bzw. prüfen...
  163. int _index = _name.lastIndexOf('.');
  164. if (_index != -1) {
  165. String _pkgname = _name.substring(0, _index);
  166. Package _package = this.getPackage(_pkgname);
  167. if (_package == null) {
  168. URL _packageUrl = getResource(_pkgname.replaceAll("\\.", "/").concat("/"));
  169. Manifest _manifest = this.manifests.get(_nestedJarPath);
  170. if (_manifest != null) {
  171. definePackage(_pkgname, _manifest, _packageUrl);
  172. } else {
  173. definePackage(_pkgname, null, null, null, null, null, null, null);
  174. }
  175. }
  176. }
  177. // Klasse anlegen...
  178. byte[] data = new byte[1024];
  179. int read = 0;
  180.  
  181. byte[] _byteCode = null;
  182. try {
  183. ByteArrayOutputStream _byteArrayOutputStream = new ByteArrayOutputStream();
  184. while ((read = _byteCodeStream.read(data, 0, 1024)) != -1) {
  185. _byteArrayOutputStream.write(data, 0, read);
  186. }
  187. _byteCode = _byteArrayOutputStream.toByteArray();
  188. Class _class = defineClass(_name, _byteCode, 0, _byteCode.length);
  189. this.resolveClass(_class);
  190. return _class;
  191.  
  192. } catch (IOException e) {
  193. throw new ClassNotFoundException(_name);
  194. }
  195.  
  196. } else
  197. throw new ClassNotFoundException(_name);
  198. }
  199.  
  200. /**
  201.   * Dieser Überschrieb dient zur einbindung von <code>_findClass()</code>.
  202.   * @override
  203.   * @param name Klassenname
  204.   * @return die passende Klasse
  205.   * @throws ClassNotFoundException
  206.   */
  207. protected Class<?> findClass(String name) throws ClassNotFoundException {
  208. try {
  209. return super.findClass(name);
  210. } catch (ClassNotFoundException e) {
  211. try {
  212.  
  213. return this._findClass(name);
  214. } catch (ClassNotFoundException ce) {
  215. throw new ClassNotFoundException(name);
  216. }
  217.  
  218. }
  219. }
  220.  
  221.  
  222. /**
  223.   * Gibt eine Resource als Strom aus. Der Index nutzt hier zum schnellen
  224.   * Auffinden der passenden eingebetten JAR.
  225.   * @param _name Ressourcenpfad
  226.   * @return InputStream bei Erfolg - sonst <code>null</code>
  227.   */
  228. public InputStream getResourceAsStream(String _name) {
  229. InputStream _return = super.getResourceAsStream(_name);
  230. if (_return == null) {
  231. String _nestedJarPath = this.indexMap.get(_name);
  232. if (_nestedJarPath != null) {
  233. try {
  234. ZipInputStream _zipInputStream =
  235. new ZipInputStream(super.getResourceAsStream(_nestedJarPath));
  236. ZipEntry _zipEntry = null;
  237. while ((_zipEntry = _zipInputStream.getNextEntry()) != null)
  238. if (_zipEntry.getName().equals(_name))
  239. return _zipInputStream;
  240. } catch (IOException e) {
  241. _return = null;
  242. }
  243. }
  244. }
  245. return _return;
  246. }
  247. }