/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package EDU.purdue.cs.bloat.context; import java.io.*; import java.net.*; import java.util.*; import EDU.purdue.cs.bloat.editor.*; import EDU.purdue.cs.bloat.file.*; import EDU.purdue.cs.bloat.reflect.*; import EDU.purdue.cs.bloat.util.*; /** * <code>BloatingClassLoader</code> is a Java class loader that BLOATs a class * before it is loader into a Java Virtual Machine. It loads its classes from a * set of {@link URL}s. */ public abstract class BloatingClassLoader extends URLClassLoader { /** * A ClassInfoLoader that loads classes from the same locations as this * class loader. */ ClassInfoLoader loader = new BloatingClassInfoLoader(); /** The context that is used to edit classes, etc. */ private final EditorContext context = new PersistentBloatContext(loader, false); /** * Maps ClassInfos to their committed bytes (as a ByteArrayOutputStream) */ private final Map classBytes = new HashMap(); // //////////////////// Constructors ///////////////////////// /** * Creates a new <code>BloatingClassLoader</code> that loads its classes * from a given set of URLs. */ public BloatingClassLoader(final URL[] urls) { super(urls); } /** * Creates a new <code>BloatingClassLoader</code> that loads its classes * from a given set of URLs. Before attempting to load a class, this * <code>BloatingClassLoader</code> will delegate to its parent class * loader. */ public BloatingClassLoader(final URL[] urls, final ClassLoader parent) { super(urls, parent); } /** * Before the <code>Class</code> is created, invoke {@link * #bloat(ClassEditor)}. */ protected Class findClass(final String name) throws ClassNotFoundException { final ClassInfo info = this.loader.loadClass(name); final ClassEditor ce = this.context.editClass(info); this.bloat(ce); ce.commit(); final ByteArrayOutputStream baos = (ByteArrayOutputStream) this.classBytes .get(info); Assert.isNotNull(baos, "No bytes for " + name); final byte[] bytes = baos.toByteArray(); return super.defineClass(name, bytes, 0, bytes.length); } /** * Returns a <code>ClassInfoLoader</code> that loads classes from the same * place as this <code>ClassLoader</code>. */ public ClassInfoLoader getClassInfoLoader() { return this.loader; } protected EditorContext getEditorContext() { return context; } /** * This method is invoked as a class is being loaded. */ protected abstract void bloat(ClassEditor ce); /** * This inner class is a ClassInfoLoader that loads classes from the same * locations as the outer BloatClassLoader. The primary reason that we have * this class is because the loadClass method of ClassInfoLoader has a * different signature from ClassLoader. Hence, a ClassLoader cannot be a * ClassInfoLoader. */ class BloatingClassInfoLoader implements ClassInfoLoader { public ClassInfo loadClass(final String name) throws ClassNotFoundException { final String classFileName = name.replace('.', '/') + ".class"; final InputStream is = BloatingClassLoader.this .getResourceAsStream(classFileName); if (is == null) { throw new ClassNotFoundException("Could not find class " + name); } final DataInputStream dis = new DataInputStream(is); return new ClassFile(null, this, dis); } public ClassInfo newClass(final int modifiers, final int classIndex, final int superClassIndex, final int[] interfaceIndexes, final java.util.List constants) { return new ClassFile(modifiers, classIndex, superClassIndex, interfaceIndexes, constants, this); } public OutputStream outputStreamFor(final ClassInfo info) throws IOException { // Maintain a mapping between ClassInfos and their committed bytes final OutputStream os = new ByteArrayOutputStream(); classBytes.put(info, os); return (os); } } }