import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Diese Klasse ermöglicht es auf Klassen und Resourcen in einer JAR und
* in einer JAR, die in der JAR liegt zuzugreifen. Dazu wird NICHT ausgelagert
* und zwischengespeichert. Beim ersten Laden einer Klassen kann es bei
* doppelter Verpackung (zip) zu Verzögerungen kommen.
*
* LICENCE: Diese Klasse darf in nicht kommerziellen Projekten verwendet
* werden. Erweiterungen, Verbesserungen sollten, wenn möglich zu mir zurück
* fliesen, um eine einheitliche Weiterentwicklung zu gewährleisten.
*
* NEUGIER: Wenn jemand diese Klasse verwendet, wäre ich über Rückmeldungen
* sehr dankbar.
*
* @author Clemens Gogolin (Feb. 2008, Braunschweig, Germany)
* @version 0.9b
*/
// Liste aller eingebetter JARs (Liste weil geordnet!)
private List<String> nestedJarPath = new ArrayList<String>();
// Relation: eingebetter JARs <-> Manifest
private Map<String, Manifest> manifests = new HashMap<String, Manifest>();
// URL des PlugIn-Container-JAR
private URL plugInURL
= null;
// Index-Map
private Map<String, String> indexMap = new HashMap<String, String>();
// Logging
static Logger logger = Logger.getLogger(PlugInClassLoader.class.getName());
// Logging-Switch
static Level level = Level.OFF;
public PlugInClassLoader
(URL _url
) { super(new URL[] { _url
}); this.plugInURL = _url;
logger.setLevel(level);
}
super(new URL[] { _url
}, parent
); this.plugInURL = _url;
logger.setLevel(level);
}
/**
* Hinzufügen eines einbegetteten JARs und diese JAR wird indiziert.
* @param _nestedJarPath realtiver Pfad im JAR auf ein JAR (ohne / zu Beginn!)
*/
public void addNestedJarPath
(String _nestedJarPath
) { if (super.getResource(_nestedJarPath) != null) {
this.nestedJarPath.add(_nestedJarPath);
try {
_manifest
= new Manifest(this.
getResourceAsStream("META-INF/MANIFEST.MF"));
}
this.manifests.put(_nestedJarPath, _manifest);
this.indexNestedJar(_nestedJarPath);
} else
}
/**
* Entfernt eine eingebette JAR. Dann werden werden die Indexeinträge gelöscht.
* und nach gesehen ob vielleicht ein nachfolgendes JAR die passende Resource
* auch beinhaltet.
* @param _nestedJarPath realtiver Pfad im JAR auf ein JAR (ohne / zu Beginn!)
*/
public void removeNestedJarPath
(String _nestedJarPath
) { if (this.nestedJarPath.contains(_nestedJarPath)) {
int _index = this.nestedJarPath.indexOf(_nestedJarPath);
this.nestedJarPath.remove(_index);
this.manifests.remove(_nestedJarPath);
Object[] _keys
= this.
indexMap.
keySet().
toArray(); for (int _i = _index, _length = _keys.length; _i < _length; _i++)
if (_nestedJarPath.equals(this.indexMap.get(_keys[_i])))
this.indexMap.remove(_keys[_i]);
for (int _i = 0, size = this.nestedJarPath.size(); _i < size; _i++)
this.indexNestedJar(nestedJarPath.get(_i));
} else
}
/**
* Routine zum Indizieren (ganz simpel: Dateipfad -> eingebettete JAR).
* @param _nestedJarPath realtiver Pfad im JAR auf ein JAR (ohne / zu Beginn!)
*/
private void indexNestedJar
(String _nestedJarPath
) { try {
while ((_zipEntry = _zipInputStream.getNextEntry()) != null) {
_path = _zipEntry.getName();
if (!this.indexMap.containsKey(_path)) {
this.indexMap.put(_path, _nestedJarPath);
logger.log(Level.WARNING,"Index: "+_path + " => " + _nestedJarPath);
}
}
logger.log(Level.WARNING, " private void indexNestedJar(String " + _nestedJarPath + " );");
}
}
/**
* Wenn <code>super.findResource(name);</code> nix findet, wird im Index
* nach gesehen, ob diese Datei bekannt ist.!
* @param name Rourcenname
* @return Eine URL (sie hier erzeugt wurde beginnt sie mit "jar:jar:" und kann
* von Java nicht interpretiert werden!)
*/
URL _return
= super.
findResource(name
); if (_return == null) {
String _nestedJarPath
= this.
indexMap.
get(name
); if (_nestedJarPath != null) {
try {
_return
= new URL("jar:jar:" + this.
plugInURL + "!/" + _nestedJarPath
+ "!/" + name
); _return = null;
}
}
}
return _return;
}
/**
* Interne Methode zum Finden von Klassen. Dazu wir die Resource als Stream
* ausfindig gemacht und ne Klasse ganz per Hand erzeugt.
* @param _name Klassenname
* @return Die fertige Klasse
* @throws ClassNotFoundException Doch nicht gefunden
*/
String _resourcePath
= _name.
replaceAll("\\.",
"/").
concat(".class"); InputStream _byteCodeStream
= getResourceAsStream
(_resourcePath
); if (_byteCodeStream != null) {
String _nestedJarPath
= this.
indexMap.
get(_resourcePath
); // Package anlegen bzw. prüfen...
int _index = _name.lastIndexOf('.');
if (_index != -1) {
String _pkgname
= _name.
substring(0, _index
); Package _package = this.getPackage(_pkgname);
if (_package == null) {
URL _packageUrl
= getResource
(_pkgname.
replaceAll("\\.",
"/").
concat("/")); Manifest _manifest
= this.
manifests.
get(_nestedJarPath
); if (_manifest != null) {
definePackage(_pkgname, _manifest, _packageUrl);
} else {
definePackage(_pkgname, null, null, null, null, null, null, null);
}
}
}
// Klasse anlegen...
byte[] data = new byte[1024];
int read = 0;
byte[] _byteCode = null;
try {
while ((read = _byteCodeStream.read(data, 0, 1024)) != -1) {
_byteArrayOutputStream.write(data, 0, read);
}
_byteCode = _byteArrayOutputStream.toByteArray();
Class _class = defineClass(_name, _byteCode, 0, _byteCode.length);
this.resolveClass(_class);
return _class;
}
} else
}
/**
* Dieser Überschrieb dient zur einbindung von <code>_findClass()</code>.
* @override
* @param name Klassenname
* @return die passende Klasse
* @throws ClassNotFoundException
*/
try {
return super.findClass(name);
try {
return this._findClass(name);
}
}
}
/**
* Gibt eine Resource als Strom aus. Der Index nutzt hier zum schnellen
* Auffinden der passenden eingebetten JAR.
* @param _name Ressourcenpfad
* @return InputStream bei Erfolg - sonst <code>null</code>
*/
InputStream _return
= super.
getResourceAsStream(_name
); if (_return == null) {
String _nestedJarPath
= this.
indexMap.
get(_name
); if (_nestedJarPath != null) {
try {
while ((_zipEntry = _zipInputStream.getNextEntry()) != null)
if (_zipEntry.getName().equals(_name))
return _zipInputStream;
_return = null;
}
}
}
return _return;
}
}