Skip to content

Instantly share code, notes, and snippets.

@kawasima
Created September 21, 2012 05:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kawasima/3759828 to your computer and use it in GitHub Desktop.
Save kawasima/3759828 to your computer and use it in GitHub Desktop.
Tomcat server with reporting coverage in realtime
import java.util.Collection;
import net.sourceforge.cobertura.coveragedata.ClassData;
import net.sourceforge.cobertura.coveragedata.ProjectData;
import net.sourceforge.cobertura.instrument.FirstPassMethodInstrumenter;
import org.apache.log4j.Logger;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@SuppressWarnings("rawtypes")
class ClassInstrumenter extends ClassAdapter
{
private static final Logger logger = Logger
.getLogger(ClassInstrumenter.class);
private final static String hasBeenInstrumented = "net/sourceforge/cobertura/coveragedata/HasBeenInstrumented";
private Collection ignoreRegexs;
private Collection ignoreBranchesRegexs;
private ProjectData projectData;
private ClassData classData;
private String myName;
private boolean instrument = false;
public String getClassName()
{
return this.myName;
}
public boolean isInstrumented()
{
return instrument;
}
public ClassInstrumenter(ProjectData projectData, final ClassVisitor cv,
final Collection ignoreRegexs, final Collection ignoreBranchesRegexs)
{
super(cv);
this.projectData = projectData;
this.ignoreRegexs = ignoreRegexs;
this.ignoreBranchesRegexs = ignoreBranchesRegexs;
}
private boolean arrayContains(Object[] array, Object key)
{
for (int i = 0; i < array.length; i++)
{
if (array[i].equals(key))
return true;
}
return false;
}
/**
* @param name In the format
* "net/sourceforge/cobertura/coverage/ClassInstrumenter"
*/
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces)
{
this.myName = name.replace('/', '.');
this.classData = this.projectData.getOrCreateClassData(this.myName);
this.classData.setContainsInstrumentationInfo();
// Do not attempt to instrument interfaces or classes that
// have already been instrumented
if (((access & Opcodes.ACC_INTERFACE) != 0)
|| arrayContains(interfaces, hasBeenInstrumented))
{
super.visit(version, access, name, signature, superName,
interfaces);
}
else
{
instrument = true;
// Flag this class as having been instrumented
String[] newInterfaces = new String[interfaces.length + 1];
System.arraycopy(interfaces, 0, newInterfaces, 0,
interfaces.length);
newInterfaces[newInterfaces.length - 1] = hasBeenInstrumented;
super.visit(version, access, name, signature, superName,
newInterfaces);
}
}
/**
* @param source In the format "ClassInstrumenter.java"
*/
public void visitSource(String source, String debug)
{
super.visitSource(source, debug);
classData.setSourceFileName(source);
}
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature,
final String[] exceptions)
{
MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
exceptions);
if (!instrument)
return mv;
return mv == null ? null : new FirstPassMethodInstrumenter(classData, mv,
this.myName, access, name, desc, signature, exceptions, ignoreRegexs,
ignoreBranchesRegexs);
}
public void visitEnd()
{
if (instrument && classData.getNumberOfValidLines() == 0)
logger.warn("No line number information found for class "
+ this.myName
+ ". Perhaps you need to compile with debug=true?");
}
}
import java.io.InputStream;
import java.util.Collection;
import java.util.Vector;
import net.sourceforge.cobertura.coveragedata.ProjectData;
import net.sourceforge.cobertura.util.IOUtil;
import org.apache.catalina.loader.WebappClassLoader;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
@SuppressWarnings("rawtypes")
public class CoberturaClassLoader extends WebappClassLoader {
public static String instrumentedPackageName;
private Collection ignoreRegexes = new Vector();
private Collection ignoreBranchesRegexes = new Vector();
private ProjectData projectData = null;
public CoberturaClassLoader(ClassLoader parent) {
super(parent);
projectData = new ProjectData();
}
@SuppressWarnings("unchecked")
public Class loadClass(String className, boolean resolve)
throws ClassNotFoundException {
Class clazz = findLoadedClass(className);
if (clazz != null) {
return clazz;
}
if (className.startsWith(instrumentedPackageName)) {
return defineClass(className, resolve);
} else {
return getParent().loadClass(className);
}
}
private Class defineClass(String className, boolean resolve) throws ClassNotFoundException {
Class clazz;
String path = className.replace('.', '/') + ".class";;
InputStream is = parent.getResourceAsStream(path);
ClassWriter cw;
ClassInstrumenter cv;
try {
ClassReader cr = new ClassReader(is);
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cv = new ClassInstrumenter(projectData, cw, ignoreRegexes, ignoreBranchesRegexes);
cr.accept(cv, 0);
} catch(Throwable t) {
throw new ClassNotFoundException(t.getMessage());
} finally {
IOUtil.closeInputStream(is);
}
if (is != null) {
clazz = defineClass(className, cw.toByteArray());
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
return null;
}
protected Class defineClass(String className, byte[] bytes) {
return defineClass(className, bytes, 0, bytes.length);
}
public ProjectData getProjectData() {
return projectData;
}
}
import java.io.File;
import net.sourceforge.cobertura.coveragedata.ProjectData;
import net.sourceforge.cobertura.coveragedata.TouchCollector;
import net.sourceforge.cobertura.reporting.ComplexityCalculator;
import net.sourceforge.cobertura.reporting.html.HTMLReport;
import net.sourceforge.cobertura.util.FileFinder;
import org.apache.catalina.Context;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardServer;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat;
public class EmbeddedServer {
private static final int PORT = 8082;
private static final String CONTEXT_PATH = "/myapp";
private static final File COVERAGE_REPORT_DIR = new File("target/coverage");
private static final String INSTRUMENTED_PACKAGE_NAME = "com.example";
private static final int REPORT_INTERVAL_SECONDS = 30;
public static void main(String[] args) throws Exception {
String appBase = new File("src/main/webapp").getAbsolutePath();
Tomcat tomcat = new Tomcat();
tomcat.setPort(PORT);
tomcat.setBaseDir(".");
tomcat.getHost().setAppBase(appBase);
StandardServer server = (StandardServer) tomcat.getServer();
AprLifecycleListener listener = new AprLifecycleListener();
server.addLifecycleListener(listener);
Context context = tomcat.addWebapp(CONTEXT_PATH, appBase);
WebappLoader webappLoader = new WebappLoader();
CoberturaClassLoader.instrumentedPackageName = INSTRUMENTED_PACKAGE_NAME;
webappLoader.setLoaderClass(CoberturaClassLoader.class.getName());
webappLoader.setDelegate(((StandardContext) context).getDelegate());
context.setLoader(webappLoader);
new CoverageMonitor(webappLoader).start();
tomcat.start();
tomcat.getServer().await();
}
public static class CoverageMonitor extends Thread {
private WebappLoader webappLoader;
ComplexityCalculator complexity;
FileFinder finder;
public CoverageMonitor(WebappLoader webappLoader) {
this.webappLoader = webappLoader;
finder = new FileFinder();
finder.addSourceDirectory("src/main/java");
complexity = new ComplexityCalculator(finder);
}
@Override
public void run() {
while(true) {
CoberturaClassLoader cl = (CoberturaClassLoader)webappLoader.getClassLoader();
if (cl != null) {
ProjectData data = cl.getProjectData();
TouchCollector.applyTouchesOnProjectData(data);
try {
new HTMLReport(data, COVERAGE_REPORT_DIR, finder, complexity, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(REPORT_INTERVAL_SECONDS * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment