Functional JarClassLoader - Warning, Really Awful
Join the DZone community and get the full member experience.
Join For FreeSome really awful functional style Java...
Note that this depends on a bunch of other classes in the playground namespace. If you actually want to use it ask me and I'll send you the source - I didn't really feel like snippetting all the needed code.
package playground.library.classloader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import playground.library.functional.FunctionalUtils;
import playground.library.functional.Pair;
import playground.library.functional.iterator.FilterIterator;
import playground.library.functional.iterator.IteratorTransformer;
import playground.library.functional.Predicate;
import playground.library.functional.Transformer;
import playground.library.functional.iterator.DelegatingIterator;
import playground.library.functional.iterator.LinkingIterator;
import playground.library.jar.JarEntryIterator;
import playground.library.utils.IteratorUtils;
import playground.library.utils.IOUtils;
/**
* ClassLoader for loading from Jar files.
*
* @author david
*/
public class JarClassLoader extends ClassLoader
{
public JarClassLoader(ClassLoader parent) { super(parent); }
public JarClassLoader() { super(); }
private final List files = new ArrayList();
/**
* Provides an iterator over all pairs of (file, entry) of JarFiles associated
* with this class loader and entries of those jar files.
*/
private Iterator> getResources(){
return new LinkingIterator>(
new IteratorTransformer>>(
new Transformer>>(){
public Iterator> transform(JarFile file){
return FunctionalUtils.merge(file, new JarEntryIterator(file));}})
.transform(JarClassLoader.this.files.iterator()));}
/**
* Searches through the available jar files to try and find a class with the given name.
*/
@Override
protected Class> findClass(String name) throws ClassNotFoundException{
final Iterator> resources = this.getResources();
while (resources.hasNext()){
final Pair resource = resources.next();
if(resource.second.getName().equals(name)){
try { return this.load(resource.first.getInputStream(resource.second));}
catch (IOException e) { e.printStackTrace(); }}}
throw new ClassNotFoundException();}
/**
* Loads the contents of the InputStream as a class. This doesn't validate
* the contents, so will fail messily if you pass it something that isn't a
* valid class file.
*/
private Class> load(final InputStream input){
try {
final ByteArrayOutputStream output = new ByteArrayOutputStream();
final byte[] bytes = IOUtils.copy(input, new ByteArrayOutputStream()).toByteArray();
return this.defineClass(null, bytes, 0, bytes.length); }
catch (IOException e){ throw new RuntimeException(e); }
finally{
try{ input.close(); }
catch (IOException e){ throw new RuntimeException(e);}}}
/**
* Provides an iterator which lazily loads the contents of the jar file as
* classes. Will also register the jar file with the class loader's file list.
* When the iterator is exhausted the jar file will be removed from the file list.
*/
public Iterator> load(final JarFile file) throws IOException{
this.files.add(file);
return
new DelegatingIterator>(
new IteratorTransformer>(
new Transformer>(){
public Class transform(JarEntry stream){
InputStream fileStream = null;
try{return JarClassLoader.this.load(fileStream = file.getInputStream(stream)); }
catch(IOException e){throw new RuntimeException(e);}
finally{IOUtils.close(fileStream); }}})
.transform(
new FilterIterator(
new Predicate(){
public boolean satisfies(JarEntry entry){
return entry.getName().endsWith(".class");}},
new JarEntryIterator(file)))){
@Override
public Class> next(){
final Class> clazz = super.next();
if (!this.hasNext()) JarClassLoader.this.files.remove(file);
return clazz;}
@Override
public boolean hasNext(){
final boolean hasNext = super.hasNext();
if (!hasNext) JarClassLoader.this.files.remove(file);
return hasNext;}};}
/**
* Loads all class files from the jar and returns a set of them.
*/
public Set> loadAll(final JarFile file) throws IOException
{
return IteratorUtils.fill(new HashSet>(), this.load(file));
}
}
Opinions expressed by DZone contributors are their own.
Comments