blob: 86b819d0b6558b5b04ab1729f57445ad1fdbf570 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.dubbo.common.bytecode;
import org.apache.dubbo.common.utils.ClassUtils;
import org.apache.dubbo.common.utils.ReflectUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
/**
* Mixin
*/
public abstract class Mixin {
private static final String PACKAGE_NAME = Mixin.class.getPackage().getName();
private static AtomicLong MIXIN_CLASS_COUNTER = new AtomicLong(0);
protected Mixin() {
}
/**
* mixin interface and delegates.
* all class must be public.
*
* @param ics interface class array.
* @param dc delegate class.
* @return Mixin instance.
*/
public static Mixin mixin(Class<?>[] ics, Class<?> dc) {
return mixin(ics, new Class[]{dc});
}
/**
* mixin interface and delegates.
* all class must be public.
*
* @param ics interface class array.
* @param dc delegate class.
* @param cl class loader.
* @return Mixin instance.
*/
public static Mixin mixin(Class<?>[] ics, Class<?> dc, ClassLoader cl) {
return mixin(ics, new Class[]{dc}, cl);
}
/**
* mixin interface and delegates.
* all class must be public.
*
* @param ics interface class array.
* @param dcs delegate class array.
* @return Mixin instance.
*/
public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs) {
return mixin(ics, dcs, ClassUtils.getCallerClassLoader(Mixin.class));
}
/**
* mixin interface and delegates.
* all class must be public.
*
* @param ics interface class array.
* @param dcs delegate class array.
* @param cl class loader.
* @return Mixin instance.
*/
public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs, ClassLoader cl) {
assertInterfaceArray(ics);
long id = MIXIN_CLASS_COUNTER.getAndIncrement();
String pkg = null;
ClassGenerator ccp = null, ccm = null;
try {
ccp = ClassGenerator.newInstance(cl);
// impl constructor
StringBuilder code = new StringBuilder();
for (int i = 0; i < dcs.length; i++) {
if (!Modifier.isPublic(dcs[i].getModifiers())) {
String npkg = dcs[i].getPackage().getName();
if (pkg == null) {
pkg = npkg;
} else {
if (!pkg.equals(npkg)) {
throw new IllegalArgumentException("non-public interfaces class from different packages");
}
}
}
ccp.addField("private " + dcs[i].getName() + " d" + i + ";");
code.append('d').append(i).append(" = (").append(dcs[i].getName()).append(")$1[").append(i).append("];\n");
if (MixinAware.class.isAssignableFrom(dcs[i])) {
code.append('d').append(i).append(".setMixinInstance(this);\n");
}
}
ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{Object[].class}, code.toString());
Class<?> neighbor = null;
// impl methods.
Set<String> worked = new HashSet<String>();
for (int i = 0; i < ics.length; i++) {
if (!Modifier.isPublic(ics[i].getModifiers())) {
String npkg = ics[i].getPackage().getName();
if (pkg == null) {
pkg = npkg;
neighbor = ics[i];
} else {
if (!pkg.equals(npkg)) {
throw new IllegalArgumentException("non-public delegate class from different packages");
}
}
}
ccp.addInterface(ics[i]);
for (Method method : ics[i].getMethods()) {
if ("java.lang.Object".equals(method.getDeclaringClass().getName())) {
continue;
}
String desc = ReflectUtils.getDesc(method);
if (worked.contains(desc)) {
continue;
}
worked.add(desc);
int ix = findMethod(dcs, desc);
if (ix < 0) {
throw new RuntimeException("Missing method [" + desc + "] implement.");
}
Class<?> rt = method.getReturnType();
String mn = method.getName();
if (Void.TYPE.equals(rt)) {
ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),
"d" + ix + "." + mn + "($$);");
} else {
ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),
"return ($r)d" + ix + "." + mn + "($$);");
}
}
}
if (pkg == null) {
pkg = PACKAGE_NAME;
neighbor = Mixin.class;
}
// create MixinInstance class.
String micn = pkg + ".mixin" + id;
ccp.setClassName(micn);
ccp.toClass(neighbor);
// create Mixin class.
String fcn = Mixin.class.getName() + id;
ccm = ClassGenerator.newInstance(cl);
ccm.setClassName(fcn);
ccm.addDefaultConstructor();
ccm.setSuperClass(Mixin.class.getName());
ccm.addMethod("public Object newInstance(Object[] delegates){ return new " + micn + "($1); }");
Class<?> mixin = ccm.toClass(Mixin.class);
return (Mixin) mixin.getDeclaredConstructor().newInstance();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
// release ClassGenerator
if (ccp != null) {
ccp.release();
}
if (ccm != null) {
ccm.release();
}
}
}
private static int findMethod(Class<?>[] dcs, String desc) {
Class<?> cl;
Method[] methods;
for (int i = 0; i < dcs.length; i++) {
cl = dcs[i];
methods = cl.getMethods();
for (Method method : methods) {
if (desc.equals(ReflectUtils.getDesc(method))) {
return i;
}
}
}
return -1;
}
private static void assertInterfaceArray(Class<?>[] ics) {
for (int i = 0; i < ics.length; i++) {
if (!ics[i].isInterface()) {
throw new RuntimeException("Class " + ics[i].getName() + " is not a interface.");
}
}
}
/**
* new Mixin instance.
*
* @param ds delegates instance.
* @return instance.
*/
abstract public Object newInstance(Object[] ds);
public static interface MixinAware {
void setMixinInstance(Object instance);
}
}