/*
* Copyright 2013 The Skfiy Open Association.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.skfiy.typhon.dispatcher;
import org.skfiy.typhon.Container;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.skfiy.typhon.packet.Packet;
/**
*
* @author Kevin Zou <kevinz@skfiy.org>
*/
@Singleton
public class AsmDispatcherFactory extends ClassLoader
implements DispatcherFactory, Opcodes {
@Inject
private Container container;
private ActionHelper helper;
private Dispatcher dispatcher;
public AsmDispatcherFactory() {
super(Dispatcher.class.getClassLoader());
}
@Override
public synchronized Dispatcher getDispatcher() {
if (dispatcher != null) {
return dispatcher;
}
helper = new ActionHelper(container);
String packageName = this.getClass().getPackage().getName();
String simpleName = "__Typhon__AsmDispatcher__";
String proxyClassName = packageName + "." + simpleName;
String inertalName = proxyClassName.replaceAll("\\.", "/");
ClassWriter cw = new ClassWriter(0);
cw.visit(V1_7, ACC_PUBLIC, inertalName, null,
Type.getInternalName(Object.class),
new String[]{Type.getInternalName(Dispatcher.class)});
Set<Class<?>> classes = helper.getActionMappings().keySet();
// Fields
for (Class clazz : classes) {
cw.visitField(ACC_PRIVATE, buildFieldName(clazz),
Type.getDescriptor(clazz), null, null).visitEnd();
}
MethodVisitor mv;
// FIXME
mv = cw.visitMethod(ACC_PUBLIC, "<init>",
"(" + Type.getDescriptor(Container.class) + ")V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
// put field value
for (Class clazz : classes) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(Type.getType(clazz));
mv.visitMethodInsn(INVOKEINTERFACE,
Type.getInternalName(Container.class),
"getInstance", "(Ljava/lang/Class;)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(clazz));
mv.visitFieldInsn(PUTFIELD, inertalName, buildFieldName(clazz),
Type.getDescriptor(clazz));
}
mv.visitInsn(RETURN);
mv.visitMaxs(4, 4);
mv.visitEnd();
// dispatch method
mv = cw.visitMethod(ACC_PUBLIC, "dispatch",
"(Ljava/lang/String;" + Type.getDescriptor(Packet.class) + ")V"
, null, null);
mv.visitCode();
//
Label endIfLab = new Label();
for (Map.Entry<Class<?>, List<ActionMapping>> entry
: helper.getActionMappings().entrySet()) {
for (ActionMapping am : entry.getValue()) {
mv.visitLdcInsn(am.getNs());
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String",
"equals", "(Ljava/lang/Object;)Z");
Label lab = new Label();
mv.visitJumpInsn(IFEQ, lab);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, inertalName,
buildFieldName(entry.getKey()),
Type.getDescriptor(entry.getKey()));
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(am.getPacketClass()));
mv.visitMethodInsn(INVOKEVIRTUAL,
Type.getInternalName(entry.getKey()),
am.getMethod().getName(),
"(" + Type.getDescriptor(am.getPacketClass()) + ")V");
//
mv.visitJumpInsn(GOTO, endIfLab);
mv.visitLabel(lab);
mv.visitFrame(F_SAME, 0, null, 0, null);
}
}
// else
String excepInternalName = Type.getInternalName(NoNamespaceDefException.class);
mv.visitTypeInsn(NEW, excepInternalName);
mv.visitInsn(DUP);
// =====================================================================
String sbInertalName = Type.getInternalName(StringBuilder.class);
mv.visitTypeInsn(NEW, sbInertalName);
mv.visitInsn(DUP);
mv.visitLdcInsn("Not found \"");
mv.visitMethodInsn(INVOKESPECIAL, sbInertalName, "<init>",
"(Ljava/lang/String;)V");
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, sbInertalName, "append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;");
mv.visitLdcInsn("\" namespace");
mv.visitMethodInsn(INVOKEVIRTUAL, sbInertalName, "append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;");
mv.visitMethodInsn(INVOKEVIRTUAL, sbInertalName, "toString",
"()Ljava/lang/String;");
// =====================================================================
mv.visitMethodInsn(INVOKESPECIAL, excepInternalName, "<init>",
"(Ljava/lang/String;)V");
mv.visitInsn(ATHROW);
mv.visitLabel(endIfLab);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(RETURN);
mv.visitMaxs(5, 4);
mv.visitEnd();
cw.visitEnd();
// ------------------------------------------------------------------
byte[] buf = cw.toByteArray();
Class proxyClass = defineClass(proxyClassName, buf, 0, buf.length);
try {
dispatcher = (Dispatcher) proxyClass.
getConstructor(Container.class).newInstance(container);
} catch (Exception e) {
// throw new AssertionError("", e);
e.printStackTrace();
}
return dispatcher;
}
@Override
public Class<?> getPacketClass(String ns) {
return helper.getPacketClass(ns);
}
private String buildFieldName(Class clazz) {
return clazz.getCanonicalName().replaceAll("\\.", "_");
}
}