/* * Copyright (c) 2009, 2010, James Leigh All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the openrdf.org nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ package net.enilink.composition; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import net.enilink.composition.asm.DefaultBehaviourFactory; /** * Factory class for creating Classes. * * @author James Leigh * @author Ken Wenzel */ public class ClassDefiner extends ClassLoader { private static final URL exists; static { try { exists = new URL("http://java/" + ClassDefiner.class.getName().replace('.', '/') + "#exists"); } catch (MalformedURLException e) { throw new AssertionError(e); } } private ConcurrentMap<String, byte[]> bytecodes = new ConcurrentHashMap<String, byte[]>(); private File output; /** * Creates a new Class Factory using the current context class loader. */ public ClassDefiner() { this(Thread.currentThread().getContextClassLoader()); } /** * Create a given Class Factory with the given class loader. * * @param parent */ public ClassDefiner(ClassLoader parent) { super(parent); } /** * Creates a new Class Factory using the current context class loader. */ public ClassDefiner(File dir) { this(dir, Thread.currentThread().getContextClassLoader()); } /** * Create a given Class Factory with the given class loader. * * @param parent */ public ClassDefiner(File dir, ClassLoader parent) { super(parent); this.output = dir; dir.mkdirs(); } @Override public URL getResource(String name) { if (output != null) { try { File file = new File(output, name); if (file.exists()) { return file.toURI().toURL(); } } catch (MalformedURLException e) { throw new AssertionError(e); } } if (bytecodes != null && bytecodes.containsKey(name)) { return exists; } return super.getResource(name); } @Override public InputStream getResourceAsStream(String name) { if (output != null) { File file = new File(output, name); if (file.exists()) { try { return new FileInputStream(file); } catch (FileNotFoundException e) { } } } if (bytecodes != null && bytecodes.containsKey(name)) { byte[] b = bytecodes.get(name); return new ByteArrayInputStream(b); } return getParent().getResourceAsStream(name); } public Class<?> defineClass(String name, byte[] bytecode) { String resource = name.replace('.', '/') + ".class"; if (output != null) { saveResource(resource, bytecode); } if (bytecodes != null) { bytecodes.putIfAbsent(resource, bytecode); } return defineClass(name, bytecode, 0, bytecode.length); } private void saveResource(String fileName, byte[] bytecode) { try { File file = new File(output, fileName); file.getParentFile().mkdirs(); FileOutputStream out = new FileOutputStream(file); try { out.write(bytecode); } finally { out.close(); } } catch (IOException e) { throw new AssertionError(e); } } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { if (name.startsWith(ClassResolver.PKG_PREFIX) || name.startsWith(DefaultBehaviourFactory.PKG_PREFIX)) { // prevent delegation to parent class loader, // since this class should be defined in this class loader synchronized (getClassLoadingLock(name)) { Class<?> c = findLoadedClass(name); if (c == null) { throw new ClassNotFoundException(name); } if (resolve) { resolveClass(c); } return c; } } return super.loadClass(name, resolve); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { // required to load classes from this bundle or from other (dynamically) // imported bundles return getClass().getClassLoader().loadClass(name); } }