/* * Copyright 1999-2011 Alibaba Group. * * 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 com.alibaba.dubbo.common.bytecode; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.CtNewConstructor; import javassist.CtNewMethod; import javassist.LoaderClassPath; import javassist.NotFoundException; import com.alibaba.dubbo.common.utils.ReflectUtils; /** * ClassGenerator * * @author qian.lei */ public final class ClassGenerator { public static interface DC{} // dynamic class tag interface. private static final AtomicLong CLASS_NAME_COUNTER = new AtomicLong(0); private static final String SIMPLE_NAME_TAG = "<init>"; private static final Map<ClassLoader, ClassPool> POOL_MAP = new ConcurrentHashMap<ClassLoader, ClassPool>(); //ClassLoader - ClassPool public static ClassGenerator newInstance() { return new ClassGenerator(getClassPool(Thread.currentThread().getContextClassLoader())); } public static ClassGenerator newInstance(ClassLoader loader) { return new ClassGenerator(getClassPool(loader)); } public static boolean isDynamicClass(Class<?> cl) { return ClassGenerator.DC.class.isAssignableFrom(cl); } public static ClassPool getClassPool(ClassLoader loader) { if( loader == null ) return ClassPool.getDefault(); ClassPool pool = POOL_MAP.get(loader); if( pool == null ) { pool = new ClassPool(true); pool.appendClassPath(new LoaderClassPath(loader)); POOL_MAP.put(loader, pool); } return pool; } private ClassPool mPool; private CtClass mCtc; private String mClassName, mSuperClass; private Set<String> mInterfaces; private List<String> mFields, mConstructors, mMethods; private Map<String, Method> mCopyMethods; // <method desc,method instance> private Map<String, Constructor<?>> mCopyConstructors; // <constructor desc,constructor instance> private boolean mDefaultConstructor = false; private ClassGenerator(){} private ClassGenerator(ClassPool pool) { mPool = pool; } public String getClassName() { return mClassName; } public ClassGenerator setClassName(String name) { mClassName = name; return this; } public ClassGenerator addInterface(String cn) { if( mInterfaces == null ) mInterfaces = new HashSet<String>(); mInterfaces.add(cn); return this; } public ClassGenerator addInterface(Class<?> cl) { return addInterface(cl.getName()); } public ClassGenerator setSuperClass(String cn) { mSuperClass = cn; return this; } public ClassGenerator setSuperClass(Class<?> cl) { mSuperClass = cl.getName(); return this; } public ClassGenerator addField(String code) { if( mFields == null ) mFields = new ArrayList<String>(); mFields.add(code); return this; } public ClassGenerator addField(String name, int mod, Class<?> type) { return addField(name, mod, type, null); } public ClassGenerator addField(String name, int mod, Class<?> type, String def) { StringBuilder sb = new StringBuilder(); sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(type)).append(' '); sb.append(name); if( def != null && def.length() > 0 ) { sb.append('='); sb.append(def); } sb.append(';'); return addField(sb.toString()); } public ClassGenerator addMethod(String code) { if( mMethods == null ) mMethods = new ArrayList<String>(); mMethods.add(code); return this; } public ClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, String body) { return addMethod(name, mod, rt, pts, null, body); } public ClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, Class<?>[] ets, String body) { StringBuilder sb = new StringBuilder(); sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(rt)).append(' ').append(name); sb.append('('); for(int i=0;i<pts.length;i++) { if( i > 0 ) sb.append(','); sb.append(ReflectUtils.getName(pts[i])); sb.append(" arg").append(i); } sb.append(')'); if( ets != null && ets.length > 0 ) { sb.append(" throws "); for(int i=0;i<ets.length;i++) { if( i > 0 ) sb.append(','); sb.append(ReflectUtils.getName(ets[i])); } } sb.append('{').append(body).append('}'); return addMethod(sb.toString()); } public ClassGenerator addMethod(Method m) { addMethod(m.getName(), m); return this; } public ClassGenerator addMethod(String name, Method m) { String desc = name + ReflectUtils.getDescWithoutMethodName(m); addMethod(':' + desc); if( mCopyMethods == null ) mCopyMethods = new ConcurrentHashMap<String, Method>(8); mCopyMethods.put(desc, m); return this; } public ClassGenerator addConstructor(String code) { if( mConstructors == null ) mConstructors = new LinkedList<String>(); mConstructors.add(code); return this; } public ClassGenerator addConstructor(int mod, Class<?>[] pts, String body) { return addConstructor(mod, pts, null, body); } public ClassGenerator addConstructor(int mod, Class<?>[] pts, Class<?>[] ets, String body) { StringBuilder sb = new StringBuilder(); sb.append(modifier(mod)).append(' ').append(SIMPLE_NAME_TAG); sb.append('('); for(int i=0;i<pts.length;i++) { if( i > 0 ) sb.append(','); sb.append(ReflectUtils.getName(pts[i])); sb.append(" arg").append(i); } sb.append(')'); if( ets != null && ets.length > 0 ) { sb.append(" throws "); for(int i=0;i<ets.length;i++) { if( i > 0 ) sb.append(','); sb.append(ReflectUtils.getName(ets[i])); } } sb.append('{').append(body).append('}'); return addConstructor(sb.toString()); } public ClassGenerator addConstructor(Constructor<?> c) { String desc = ReflectUtils.getDesc(c); addConstructor(":"+desc); if( mCopyConstructors == null ) mCopyConstructors = new ConcurrentHashMap<String, Constructor<?>>(4); mCopyConstructors.put(desc, c); return this; } public ClassGenerator addDefaultConstructor() { mDefaultConstructor = true; return this; } public ClassPool getClassPool() { return mPool; } public Class<?> toClass(){ return toClass(getClass().getClassLoader(), getClass().getProtectionDomain()); } public Class<?> toClass(ClassLoader loader, ProtectionDomain pd) { if( mCtc != null ) mCtc.detach(); long id = CLASS_NAME_COUNTER.getAndIncrement(); try { CtClass ctcs = mSuperClass == null ? null : mPool.get(mSuperClass); if( mClassName == null ) mClassName = ( mSuperClass == null || javassist.Modifier.isPublic(ctcs.getModifiers()) ? ClassGenerator.class.getName() : mSuperClass + "$sc" ) + id; mCtc = mPool.makeClass(mClassName); if( mSuperClass != null ) mCtc.setSuperclass(ctcs); mCtc.addInterface(mPool.get(DC.class.getName())); // add dynamic class tag. if( mInterfaces != null ) for( String cl : mInterfaces ) mCtc.addInterface(mPool.get(cl)); if( mFields != null ) for( String code : mFields ) mCtc.addField(CtField.make(code, mCtc)); if( mMethods != null ) { for( String code : mMethods ) { if( code.charAt(0) == ':' ) mCtc.addMethod(CtNewMethod.copy(getCtMethod(mCopyMethods.get(code.substring(1))), code.substring(1, code.indexOf('(')), mCtc, null)); else mCtc.addMethod(CtNewMethod.make(code, mCtc)); } } if( mDefaultConstructor ) mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc)); if( mConstructors != null ) { for( String code : mConstructors ) { if( code.charAt(0) == ':' ) { mCtc.addConstructor(CtNewConstructor.copy(getCtConstructor(mCopyConstructors.get(code.substring(1))), mCtc, null)); } else { String[] sn = mCtc.getSimpleName().split("\\$+"); // inner class name include $. mCtc.addConstructor(CtNewConstructor.make(code.replaceFirst(SIMPLE_NAME_TAG, sn[sn.length-1]), mCtc)); } } } return mCtc.toClass(loader, pd); } catch(RuntimeException e) { throw e; } catch(NotFoundException e) { throw new RuntimeException(e.getMessage(), e); } catch(CannotCompileException e) { throw new RuntimeException(e.getMessage(), e); } } public void release() { if( mCtc != null ) mCtc.detach(); if( mInterfaces != null ) mInterfaces.clear(); if( mFields != null ) mFields.clear(); if( mMethods != null ) mMethods.clear(); if( mConstructors != null ) mConstructors.clear(); if( mCopyMethods != null ) mCopyMethods.clear(); if( mCopyConstructors != null ) mCopyConstructors.clear(); } private CtClass getCtClass(Class<?> c) throws NotFoundException { return mPool.get(c.getName()); } private CtMethod getCtMethod(Method m) throws NotFoundException { return getCtClass(m.getDeclaringClass()).getMethod(m.getName(),ReflectUtils.getDescWithoutMethodName(m)); } private CtConstructor getCtConstructor(Constructor<?> c) throws NotFoundException { return getCtClass(c.getDeclaringClass()).getConstructor(ReflectUtils.getDesc(c)); } private static String modifier(int mod) { if( Modifier.isPublic(mod) ) return "public"; if( Modifier.isProtected(mod) ) return "protected"; if( Modifier.isPrivate(mod) ) return "private"; return ""; } }