package net.unit8.waitt.feature.jacoco; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; import java.util.Set; import java.util.logging.Logger; import org.jacoco.core.instr.Instrumenter; import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator; /** * * @author kawasima */ public class JacocoClassLoader extends URLClassLoader { private static final Logger logger = Logger.getLogger(JacocoClassLoader.class.getName()); private static JacocoClassLoader instance; private Set<String> targetPackages; private Instrumenter instrumenter = null; private JacocoClassLoader(ClassLoader parent) { super(new URL[0], parent); initInstrumenter(); } public static synchronized JacocoClassLoader create(ClassLoader parent) { if (instance == null) { instance = new JacocoClassLoader(parent); } return instance; } public static JacocoClassLoader getInstance() { if (instance == null) { throw new IllegalStateException("CoberturaClassLoader hasn't been instantiated yet."); } return instance; } private void initInstrumenter() { instrumenter = new Instrumenter(new OfflineInstrumentationAccessGenerator()); } @SuppressWarnings("unchecked") @Override public Class loadClass(final String className, boolean resolve) throws ClassNotFoundException { Class clazz = findLoadedClass(className); if (clazz != null) { return clazz; } Boolean isTargetPackage = false; for (String pkgName : targetPackages) { isTargetPackage |= className.startsWith(pkgName); } if (isTargetPackage) { logger.fine("[ClassLoad] " + className + " from JaCoCoLoader"); return defineClass(className, resolve); } else { return getParent().loadClass(className); } } private Class defineClass(String className, boolean resolve) throws ClassNotFoundException { Class clazz = null; String path = className.replace('.', '/') + ".class"; InputStream is = getParent().getResourceAsStream(path); ByteArrayOutputStream baos = new ByteArrayOutputStream(32768); try { instrumenter.instrument(is, baos, path); clazz = defineClass(className, baos.toByteArray()); if (resolve) { resolveClass(clazz); } } catch(Throwable t) { throw new ClassNotFoundException(className + " from " + getParent(), t); } finally { try { is.close(); } catch (IOException ex) { //ignore } } return clazz; } Class defineClass(String className, byte[] bytes) { return defineClass(className, bytes, 0, bytes.length); } public Instrumenter getInstrumenter() { return instrumenter; } public void setTargetPackages(Set<String> targetPackages) { this.targetPackages = targetPackages; } }