42
Having fun with Javassist Anton Arhipov

Con-FESS 2015 - Having Fun With Javassist

Embed Size (px)

Citation preview

Having fun with Javassist

Anton Arhipov

@antonarhipov

Anton Arhipov

Why Javassist?

PrototypingSimple integrations

All framework integrationsWorkarounds / h4cKs

Why Javassist?

Bytecode manipulation

Why Javassist?

Bytecode manipulation

made easy!

Agenda

The use cases for bytecode manipulation

Code examples with Javassist

How to create your own Java agent

speakerdeck.com/antonarhipov

github.com/zeroturnaround/callspy

Bytecode manipulation

Code analysis:

Find bugs in your application

Examine code complexity

Find classes by specific attributes / annotations

Bytecode manipulation

Code generation:

Lazily load data using proxies

Add cross-cutting concerns to the code

Generate classes from non-Java artefacts

Bytecode manipulation

Main use case for Javassist: generate proxiesi.e. at runtime, create a subclass that intercepts all method invocations

Spring AOPHibernate proxies EJB proxies

CDI proxies

java.lang.reflect.Proxy is not enough

Bytecode manipulation

Transform classes without source codeCode profiling

Code optimization

Some other crazy things

Good news, everyone! We will implement a logger today!

public void doSomething(){ stmt; stmt; stmt; stmt; stmt; }

public void doSomething(){ // no logging here stmt; stmt; stmt; stmt; }

public void doSomething(){ // no logging here // argh!!! stmt; stmt; stmt; }

If only I could add more logging here…

Instrument all the bytecode!

… with Javassist!

Javassist

Bytecode manipulation made easy

Source level and bytecode level APIs

Uses vocabulary of a Java language

On-the-fly compilation of injected codewww.javassist.org

CtPool

CtClassCtClassCtClass

CtClass

CtFieldCtMethodCtConst

CtMethod

insertBeforeinsertAfterinstrument

Javassist

-javaagent

import java.lang.instrument.ClassFileTransformer;import java.lang.instrument.Instrumentation;

public class Agent {

public static void premain(String args, Instrumentation inst) { inst.addTransformer(new ClassFileTransformer { … }); }

public static void agentmain(String args, Instrumentation inst) { premain(args, inst); }}

public static void premain(String args, Instrumentation inst)

java -javaagent:<jarfile>[=options]

META-INF/MANIFEST.MF is required

public static void agentmain(String args, Instrumentation inst)

Executed when the agent attaches to the running JVM

META-INF/MANIFEST.MF is required

Requires support for loading agents in JVM

Allows adding the code to JVM post-factum

Manifest-Version: 1.0 Ant-Version: Apache Ant 1.8.1 Created-By: 1.7.0_15-b03 (Oracle Corporation) Premain-Class: com.zeroturnaround.javarebel.java5.AgentInstall Can-Redefine-Classes: true Boot-Class-Path: jrebel.jar Main-Class: com.zeroturnaround.javarebel.java4.Install Implementation-Title: JRebel Specification-Title: jrebel Specification-Version: 5.4.0 Implementation-Version: 201309021535

Manifest.mf

import com.sun.tools.attach.VirtualMachine;

//attach to target VMVirtualMachine vm = VirtualMachine.attach("2177");

//get system properties in the target VMProperties props = vm.getSystemProperties();

//load agent into the VMvm.loadAgent("agent.jar", "arg1=x,arg2=y");

//detach from VMvm.detach();

http://docs.oracle.com/javase/7/docs/jdk/api/attach/spec/com/sun/tools/attach/VirtualMachine.html

JVM JVM

Process 1 Process 2

Agent

-cp tools.jar

Instrumentation API

Instrumentation API

void addTransformer(ClassFileTransformer transformer, boolean canRetransform);

void appendToBootstrapClassLoaderSearch(JarFile jarfile);void appendToSystemClassLoaderSearch(JarFile jarfile);

Class[] getAllLoadedClasses();Class[] getInitiatedClasses(ClassLoader loader); void redefineClasses(ClassDefinition... classes);void retransformClasses(Class<?>... classes);

byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain pd, byte[] classfileBuffer);

ClassFileTransformer

public class MyTransformer implements ClassFileTransformer {

public void byte[] transform(ClassLoader loader, String className, Class<?> toRefine, ProtectionDomain pd, byte[] classfileBuffer){

ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer));

// transform the bytes as required, // for instance - with Javassist return ct.toBytecode(); }}

Class loading

Instrumentation

Class loading

Instrumentation ClassFileTransformer

ClassFileTransformer

ClassFileTransformer

Class loading

Instrumentation ClassFileTransformer

ClassFileTransformer

ClassFileTransformer

Javassist

ASM

JRebel core

Spring plugin

Hibernate plugin

EJB plugin

JRebel core

Spring plugin

Hibernate plugin

EJB plugin

Reloads classes

JRebel core

Spring plugin

Hibernate plugin

EJB plugin

Notifies plugins

JRebel core

Spring plugin

Hibernate plugin

EJB plugin

Reload configurations

JRebel core

Spring plugin

Hibernate plugin

EJB plugin

Implemented with Javassist