/* | |
* 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.aries.proxy.impl.interfaces; | |
import java.io.IOException; | |
import java.lang.reflect.Modifier; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.List; | |
import org.apache.aries.proxy.UnableToProxyException; | |
import org.apache.aries.proxy.impl.ProxyUtils; | |
import org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter; | |
import org.apache.aries.proxy.impl.common.OSGiFriendlyClassVisitor; | |
import org.apache.aries.proxy.impl.common.OSGiFriendlyClassWriter; | |
import org.objectweb.asm.ClassVisitor; | |
import org.objectweb.asm.ClassWriter; | |
import org.objectweb.asm.MethodVisitor; | |
import org.objectweb.asm.Opcodes; | |
import org.objectweb.asm.Type; | |
import org.objectweb.asm.commons.Method; | |
/** | |
* This class is used to aggregate several interfaces into a real class which implements all of them | |
*/ | |
final class InterfaceCombiningClassAdapter extends ClassVisitor implements Opcodes { | |
/** The superclass we should use */ | |
private final Class<?> superclass; | |
/** The interfaces we need to implement */ | |
private final Collection<Class<?>> interfaces; | |
/** The {@link ClassWriter} we use to write our class */ | |
private final ClassWriter writer; | |
/** The adapter we use to weave in our method implementations */ | |
private final AbstractWovenProxyAdapter adapter; | |
/** Whether we have already written the class bytes */ | |
private boolean done = false; | |
/** | |
* Construct an {@link InterfaceCombiningClassAdapter} to combine the supplied | |
* interfaces into a class with the supplied name using the supplied classloader | |
* @param className | |
* @param loader | |
* @param interfaces | |
*/ | |
InterfaceCombiningClassAdapter(String className, | |
ClassLoader loader, Class<?> superclass, Collection<Class<?>> interfaces) { | |
super(Opcodes.ASM5); | |
writer = new OSGiFriendlyClassWriter(ClassWriter.COMPUTE_FRAMES, loader); | |
ClassVisitor cv = new OSGiFriendlyClassVisitor(writer, ClassWriter.COMPUTE_FRAMES); | |
adapter = new InterfaceUsingWovenProxyAdapter(cv, className, loader); | |
this.interfaces = interfaces; | |
this.superclass = superclass; | |
String[] interfaceNames = new String[interfaces.size()]; | |
int i = 0; | |
for(Class<?> in : interfaces) { | |
interfaceNames[i] = Type.getInternalName(in); | |
i++; | |
} | |
adapter.visit(ProxyUtils.getWeavingJavaVersion(), ACC_PUBLIC | ACC_SYNTHETIC, className, null, | |
(superclass == null) ? AbstractWovenProxyAdapter.OBJECT_TYPE.getInternalName() : | |
Type.getInternalName(superclass), interfaceNames); | |
} | |
@Override | |
public final MethodVisitor visitMethod(int access, String name, String desc, | |
String sig, String[] arg4) { | |
//If we already implement this method (from another interface) then we don't | |
//want a duplicate. We also don't want to copy any static init blocks (these | |
//initialize static fields on the interface that we don't copy | |
if(adapter.getKnownMethods().contains(new Method(name, desc)) || | |
"<clinit>".equals(name)) { | |
return null; | |
} | |
else if(((access & (ACC_PRIVATE|ACC_SYNTHETIC)) == (ACC_PRIVATE|ACC_SYNTHETIC))) { | |
// private, synthetic methods on interfaces don't need to be proxied. | |
return null; | |
} else if (((access & (ACC_STATIC)) == (ACC_STATIC))) { | |
//static methods on interfaces don't need to be proxied | |
return null; | |
} | |
else {//We're going to implement this method, so make it non abstract! | |
return adapter.visitMethod(access, name, desc, null, arg4); | |
} | |
} | |
/** | |
* Generate the byte[] for our class | |
* @return | |
* @throws UnableToProxyException | |
*/ | |
final byte[] generateBytes() throws UnableToProxyException { | |
if(!!!done) { | |
for(Class<?> c : interfaces) { | |
adapter.setCurrentMethodDeclaringType(Type.getType(c), true); | |
try { | |
AbstractWovenProxyAdapter.readClass(c, this); | |
} catch (IOException e) { | |
throw new UnableToProxyException(c, e); | |
} | |
} | |
Class<?> clazz = superclass; | |
while(clazz != null && (clazz.getModifiers() & Modifier.ABSTRACT) != 0) { | |
adapter.setCurrentMethodDeclaringType(Type.getType(clazz), false); | |
visitAbstractMethods(clazz); | |
clazz = clazz.getSuperclass(); | |
} | |
adapter.setCurrentMethodDeclaringType(AbstractWovenProxyAdapter.OBJECT_TYPE, false); | |
visitObjectMethods(); | |
adapter.visitEnd(); | |
done = true; | |
} | |
return writer.toByteArray(); | |
} | |
private void visitAbstractMethods(Class<?> clazz) { | |
for(java.lang.reflect.Method m : clazz.getDeclaredMethods()) { | |
int modifiers = m.getModifiers(); | |
if((modifiers & Modifier.ABSTRACT) != 0) { | |
List<String> exceptions = new ArrayList<String>(); | |
for(Class<?> c : m.getExceptionTypes()) { | |
exceptions.add(Type.getInternalName(c)); | |
} | |
MethodVisitor visitor = visitMethod(modifiers, m.getName(), Method.getMethod(m).getDescriptor(), | |
null, exceptions.toArray(new String[exceptions.size()])); | |
if (visitor != null) visitor.visitEnd(); | |
} | |
} | |
} | |
/** | |
* Make sure that the three common Object methods toString, equals and hashCode are redirected to the delegate | |
* even if they are not on any of the interfaces | |
*/ | |
private void visitObjectMethods() { | |
MethodVisitor visitor = visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "toString", "()Ljava/lang/String;", null, null); | |
if (visitor != null) visitor.visitEnd(); | |
visitor = visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "equals", "(Ljava/lang/Object;)Z", null, null); | |
if (visitor != null) visitor.visitEnd(); | |
visitor = visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "hashCode", "()I", null, null); | |
if (visitor != null) visitor.visitEnd(); | |
} | |
} |