001 // Copyright 2011 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry5.ioc.internal.services; 016 017 import org.apache.tapestry5.internal.plastic.PlasticInternalUtils; 018 import org.apache.tapestry5.internal.plastic.asm.Type; 019 import org.apache.tapestry5.internal.plastic.asm.tree.*; 020 import org.apache.tapestry5.ioc.Location; 021 import org.apache.tapestry5.ioc.ObjectCreator; 022 import org.apache.tapestry5.ioc.internal.util.InternalUtils; 023 import org.apache.tapestry5.ioc.services.PlasticProxyFactory; 024 import org.apache.tapestry5.plastic.*; 025 import org.slf4j.Logger; 026 027 import java.lang.reflect.Constructor; 028 import java.lang.reflect.Member; 029 import java.lang.reflect.Method; 030 import java.util.List; 031 032 public class PlasticProxyFactoryImpl implements PlasticProxyFactory 033 { 034 private final PlasticManager manager; 035 036 public PlasticProxyFactoryImpl(ClassLoader parentClassLoader, Logger logger) 037 { 038 this(PlasticManager.withClassLoader(parentClassLoader).create(), logger); 039 } 040 041 public PlasticProxyFactoryImpl(PlasticManager manager, Logger logger) 042 { 043 assert manager != null; 044 045 this.manager = manager; 046 047 if (logger != null) 048 { 049 manager.addPlasticClassListener(new PlasticClassListenerLogger(logger)); 050 } 051 } 052 053 public ClassLoader getClassLoader() 054 { 055 return manager.getClassLoader(); 056 } 057 058 public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, PlasticClassTransformer callback) 059 { 060 return manager.createProxy(interfaceType, callback); 061 } 062 063 public PlasticClassTransformation createProxyTransformation(Class interfaceType) 064 { 065 return manager.createProxyTransformation(interfaceType); 066 } 067 068 public <T> T createProxy(final Class<T> interfaceType, final ObjectCreator<T> creator, final String description) 069 { 070 assert creator != null; 071 assert InternalUtils.isNonBlank(description); 072 073 ClassInstantiator<T> instantiator = createProxy(interfaceType, new PlasticClassTransformer() 074 { 075 public void transform(PlasticClass plasticClass) 076 { 077 final PlasticField objectCreatorField = plasticClass.introduceField(ObjectCreator.class, "creator") 078 .inject(creator); 079 080 PlasticMethod delegateMethod = plasticClass.introducePrivateMethod(interfaceType.getName(), "delegate", 081 null, null); 082 083 delegateMethod.changeImplementation(new InstructionBuilderCallback() 084 { 085 public void doBuild(InstructionBuilder builder) 086 { 087 builder.loadThis().getField(objectCreatorField); 088 builder.invoke(ObjectCreator.class, Object.class, "createObject"); 089 builder.checkcast(interfaceType).returnResult(); 090 } 091 }); 092 093 for (Method method : interfaceType.getMethods()) 094 { 095 plasticClass.introduceMethod(method).delegateTo(delegateMethod); 096 } 097 098 plasticClass.addToString(description); 099 } 100 }); 101 102 return interfaceType.cast(instantiator.newInstance()); 103 } 104 105 private ClassNode readClassNode(Class clazz) 106 { 107 byte[] bytecode = PlasticInternalUtils.readBytecodeForClass(manager.getClassLoader(), clazz.getName(), false); 108 109 return bytecode == null ? null : PlasticInternalUtils.convertBytecodeToClassNode(bytecode); 110 } 111 112 public Location getMethodLocation(Method method) 113 { 114 return getMemberLocation(method, method.getName(), Type.getMethodDescriptor(method), 115 InternalUtils.asString(method)); 116 } 117 118 public Location getConstructorLocation(Constructor constructor) 119 { 120 StringBuilder builder = new StringBuilder(constructor.getDeclaringClass().getName()).append("("); 121 String sep = ""; 122 123 for (Class parameterType : constructor.getParameterTypes()) 124 { 125 builder.append(sep); 126 builder.append(parameterType.getSimpleName()); 127 128 sep = ", "; 129 } 130 131 builder.append(")"); 132 133 String constructorDescription = builder.toString(); 134 135 return getMemberLocation(constructor, "<init>", Type.getConstructorDescriptor(constructor), 136 constructorDescription); 137 } 138 139 public Location getMemberLocation(Member member, String methodName, String memberTypeDesc, String textDescription) 140 { 141 ClassNode classNode = readClassNode(member.getDeclaringClass()); 142 143 if (classNode == null) 144 { 145 throw new RuntimeException(String.format("Unable to read class file for %s (to gather line number information).", 146 textDescription)); 147 } 148 149 for (MethodNode mn : (List<MethodNode>) classNode.methods) 150 { 151 if (mn.name.equals(methodName) && mn.desc.equals(memberTypeDesc)) 152 { 153 int lineNumber = findFirstLineNumber(mn.instructions); 154 155 // If debugging info is not available, we may lose the line number data, in which case, 156 // just generate the Location from the textDescription. 157 158 if (lineNumber < 1) 159 { 160 break; 161 } 162 163 String description = String.format("%s (at %s:%d)", textDescription, classNode.sourceFile, lineNumber); 164 165 return new StringLocation(description, lineNumber); 166 } 167 } 168 169 // Didn't find it. Odd. 170 171 return new StringLocation(textDescription, 0); 172 } 173 174 private int findFirstLineNumber(InsnList instructions) 175 { 176 for (AbstractInsnNode node = instructions.getFirst(); node != null; node = node.getNext()) 177 { 178 if (node instanceof LineNumberNode) 179 { 180 return ((LineNumberNode) node).line; 181 } 182 } 183 184 return -1; 185 } 186 187 public void addPlasticClassListener(PlasticClassListener listener) 188 { 189 manager.addPlasticClassListener(listener); 190 } 191 192 public void removePlasticClassListener(PlasticClassListener listener) 193 { 194 manager.removePlasticClassListener(listener); 195 } 196 197 }