package org.unbrokendome.eventbus.proxy;
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Type;
import org.springframework.cglib.core.*;
import org.springframework.messaging.Message;
import org.unbrokendome.eventbus.EventSubscriber;
public class CglibSubscriberProxyClassGenerator implements SubscriberProxyClassGenerator {
@Override
public Class<? extends EventSubscriber> generate(EventSubscriberInfo subscriberInfo, ClassLoader classLoader) {
return new Generator(subscriberInfo, classLoader)
.create();
}
private static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(CglibSubscriberProxyClassGenerator.class.getName());
private static final Type MESSAGE_TYPE = Type.getType(Message.class);
private static final Signature GETPAYLOAD_SIGNATURE =
new Signature("getPayload", Constants.TYPE_OBJECT, Constants.TYPES_EMPTY);
private final EventSubscriberInfo subscriberInfo;
private final ClassLoader classLoader;
private Generator(EventSubscriberInfo subscriberInfo, ClassLoader classLoader) {
super(SOURCE);
this.subscriberInfo = subscriberInfo;
this.classLoader = classLoader;
}
@Override
protected ClassLoader getDefaultClassLoader() {
return classLoader;
}
@Override
protected Object firstInstance(Class type) throws Exception {
return type;
}
@Override
protected Object nextInstance(Object instance) throws Exception {
return instance;
}
@SuppressWarnings("unchecked")
public Class<? extends EventSubscriber> create() {
return (Class<? extends EventSubscriber>) super.create(generateKey());
}
private String generateKey() {
return Hashing.goodFastHash(32).newHasher()
.putString(subscriberInfo.getBeanClass().getName(), Charsets.UTF_8)
.putString(subscriberInfo.getSubscriberMethodName(), Charsets.UTF_8)
.putString(subscriberInfo.getEventType().getName(), Charsets.UTF_8)
.hash()
.toString();
}
private String makeProxyClassName() {
return subscriberInfo.getBeanClass().getName() + "$$EventSubscriber_" + generateKey();
}
@Override
public void generateClass(ClassVisitor classVisitor) throws Exception {
ClassEmitter classEmitter = new ClassEmitter(classVisitor);
classEmitter.begin_class(Constants.V1_5,
Constants.ACC_PUBLIC,
makeProxyClassName(),
Constants.TYPE_OBJECT,
new Type[] { Type.getType(EventSubscriberProxy.class) },
null);
createDelegateField(classEmitter);
createConstructor(classEmitter);
createGetEventTypeMethod(classEmitter);
createIsAsyncMethod(classEmitter);
createHandleMessageMethod(classEmitter);
classEmitter.end_class();
}
private void createDelegateField(ClassEmitter classEmitter) {
classEmitter.declare_field(
Constants.ACC_PRIVATE | Constants.ACC_FINAL,
"delegate",
Type.getType(subscriberInfo.getBeanClass()),
null);
}
private void createConstructor(ClassEmitter classEmitter) {
Signature signature = new Signature(
Constants.CONSTRUCTOR_NAME, Type.VOID_TYPE,
new Type[] { Type.getType(subscriberInfo.getBeanClass()) });
CodeEmitter emitter = classEmitter.begin_method(Constants.ACC_PUBLIC, signature, null);
emitter.load_this();
emitter.dup();
emitter.super_invoke_constructor();
emitter.load_arg(0);
emitter.putfield("delegate");
emitter.return_value();
emitter.end_method();
}
private void createGetEventTypeMethod(ClassEmitter classEmitter) {
Signature signature = new Signature(
"getEventType", Constants.TYPE_CLASS, Constants.TYPES_EMPTY);
CodeEmitter emitter = classEmitter.begin_method(Constants.ACC_PUBLIC, signature, null);
emitter.visitLdcInsn(Type.getType(subscriberInfo.getEventType()));
emitter.return_value();
emitter.end_method();
}
private void createIsAsyncMethod(ClassEmitter classEmitter) {
Signature signature = new Signature(
"isAsync", Type.BOOLEAN_TYPE, Constants.TYPES_EMPTY);
CodeEmitter emitter = classEmitter.begin_method(Constants.ACC_PUBLIC, signature, null);
emitter.visitLdcInsn(subscriberInfo.isAsync() ? 1 : 0);
emitter.return_value();
emitter.end_method();
}
private void createHandleMessageMethod(ClassEmitter classEmitter) {
Signature signature = new Signature(
"handleMessage", Type.VOID_TYPE, new Type[] { MESSAGE_TYPE });
Signature subscriberMethodSignature = new Signature(
subscriberInfo.getSubscriberMethodName(),
Type.VOID_TYPE,
new Type[] { Type.getType(subscriberInfo.getEventType()) });
CodeEmitter emitter = classEmitter.begin_method(Constants.ACC_PUBLIC, signature, null);
emitter.load_this();
emitter.getfield("delegate");
emitter.load_arg(0);
emitter.invoke_interface(MESSAGE_TYPE, GETPAYLOAD_SIGNATURE);
emitter.checkcast(Type.getType(subscriberInfo.getEventType()));
emitter.invoke_virtual(Type.getType(subscriberInfo.getBeanClass()), subscriberMethodSignature);
emitter.return_value();
emitter.end_method();
}
}
}