001// Copyright 2011, 2012 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 015package org.apache.tapestry5.ioc.internal.services; 016 017import org.apache.tapestry5.internal.plastic.PlasticInternalUtils; 018import org.apache.tapestry5.internal.plastic.asm.Type; 019import org.apache.tapestry5.internal.plastic.asm.tree.*; 020import org.apache.tapestry5.ioc.Location; 021import org.apache.tapestry5.ioc.ObjectCreator; 022import org.apache.tapestry5.ioc.annotations.IncompatibleChange; 023import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 024import org.apache.tapestry5.ioc.internal.util.InternalUtils; 025import org.apache.tapestry5.ioc.services.PlasticProxyFactory; 026import org.apache.tapestry5.plastic.*; 027import org.slf4j.Logger; 028 029import java.lang.reflect.Constructor; 030import java.lang.reflect.Member; 031import java.lang.reflect.Method; 032import java.util.List; 033import java.util.Map; 034 035public class PlasticProxyFactoryImpl implements PlasticProxyFactory 036{ 037 public static final String INTERNAL_GET_DELEGATE = "_____internalGetDelegate_DONT_CALL_THIS_METHOD_____"; 038 039 private final PlasticManager manager; 040 041 private final Map<String, Location> memberToLocation = CollectionFactory.newConcurrentMap(); 042 043 public PlasticProxyFactoryImpl(ClassLoader parentClassLoader, Logger logger) 044 { 045 this(PlasticManager.withClassLoader(parentClassLoader).create(), logger); 046 } 047 048 public PlasticProxyFactoryImpl(PlasticManager manager, Logger logger) 049 { 050 assert manager != null; 051 052 this.manager = manager; 053 054 if (logger != null) 055 { 056 manager.addPlasticClassListener(new PlasticClassListenerLogger(logger)); 057 } 058 } 059 060 @Override 061 public ClassLoader getClassLoader() 062 { 063 return manager.getClassLoader(); 064 } 065 066 @Override 067 public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, Class<? extends T> implementationType, PlasticClassTransformer callback) 068 { 069 return manager.createProxy(interfaceType, implementationType, callback); 070 } 071 072 @Override 073 public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, PlasticClassTransformer callback) 074 { 075 return manager.createProxy(interfaceType, callback); 076 } 077 078 079 @Override 080 public <T> PlasticClassTransformation<T> createProxyTransformation(Class<T> interfaceType, 081 Class<? extends T> implementationType) 082 { 083 return manager.createProxyTransformation(interfaceType, implementationType); 084 } 085 086 @Override 087 public <T> PlasticClassTransformation<T> createProxyTransformation(Class<T> interfaceType) 088 { 089 return createProxyTransformation(interfaceType, null); 090 } 091 092 @Override 093 public <T> T createProxy(final Class<T> interfaceType, final ObjectCreator<T> creator, final String description) 094 { return createProxy(interfaceType, null, creator, description); 095 } 096 097 @Override 098 public <T> T createProxy(final Class<T> interfaceType, final Class<? extends T> implementationType, 099 final ObjectCreator<T> creator, final String description) 100 { 101 assert creator != null; 102 assert InternalUtils.isNonBlank(description); 103 104 ClassInstantiator<T> instantiator = createProxy(interfaceType, implementationType, new PlasticClassTransformer() 105 { 106 @Override 107 public void transform(PlasticClass plasticClass) 108 { 109 final PlasticField objectCreatorField = plasticClass.introduceField(ObjectCreator.class, "creator") 110 .inject(creator); 111 112 final String interfaceTypeName = interfaceType.getName(); 113 PlasticMethod delegateMethod = plasticClass.introducePrivateMethod(interfaceTypeName, "delegate", 114 null, null); 115 116 final InstructionBuilderCallback returnCreateObject = new InstructionBuilderCallback() 117 { 118 @Override 119 public void doBuild(InstructionBuilder builder) 120 { 121 builder.loadThis().getField(objectCreatorField); 122 builder.invoke(ObjectCreator.class, Object.class, "createObject"); 123 builder.checkcast(interfaceType).returnResult(); 124 } 125 }; 126 127 delegateMethod.changeImplementation(returnCreateObject); 128 129 for (Method method : interfaceType.getMethods()) 130 { 131 plasticClass.introduceMethod(method).delegateTo(delegateMethod); 132 } 133 134 // TA5-2235 135 MethodDescription getDelegateMethodDescription = 136 new MethodDescription(interfaceType.getName(), INTERNAL_GET_DELEGATE); 137 plasticClass.introduceMethod(getDelegateMethodDescription, returnCreateObject); 138 139 plasticClass.addToString(description); 140 141 } 142 }); 143 144 return interfaceType.cast(instantiator.newInstance()); 145 } 146 147 private ClassNode readClassNode(Class clazz) 148 { 149 byte[] bytecode = PlasticInternalUtils.readBytecodeForClass(manager.getClassLoader(), clazz.getName(), false); 150 151 return bytecode == null ? null : PlasticInternalUtils.convertBytecodeToClassNode(bytecode); 152 } 153 154 @Override 155 public Location getMethodLocation(final Method method) 156 { 157 ObjectCreator<String> descriptionCreator = new ObjectCreator<String>() 158 { 159 @Override 160 public String createObject() 161 { 162 return InternalUtils.asString(method); 163 } 164 }; 165 166 return getMemberLocation(method, method.getName(), Type.getMethodDescriptor(method), 167 descriptionCreator); 168 } 169 170 @Override 171 public Location getConstructorLocation(final Constructor constructor) 172 { 173 ObjectCreator<String> descriptionCreator = new ObjectCreator<String>() 174 { 175 @Override 176 public String createObject() 177 { 178 StringBuilder builder = new StringBuilder(constructor.getDeclaringClass().getName()).append("("); 179 String sep = ""; 180 181 for (Class parameterType : constructor.getParameterTypes()) 182 { 183 builder.append(sep); 184 builder.append(parameterType.getSimpleName()); 185 186 sep = ", "; 187 } 188 189 builder.append(")"); 190 191 return builder.toString(); 192 } 193 }; 194 195 return getMemberLocation(constructor, "<init>", Type.getConstructorDescriptor(constructor), 196 descriptionCreator); 197 } 198 199 @Override 200 public void clearCache() 201 { 202 memberToLocation.clear(); 203 } 204 205 206 public Location getMemberLocation(Member member, String methodName, String memberTypeDesc, ObjectCreator<String> textDescriptionCreator) 207 { 208 String className = member.getDeclaringClass().getName(); 209 210 String key = className + ":" + methodName + ":" + memberTypeDesc; 211 212 Location location = memberToLocation.get(key); 213 214 if (location == null) 215 { 216 location = constructMemberLocation(member, methodName, memberTypeDesc, textDescriptionCreator.createObject()); 217 218 memberToLocation.put(key, location); 219 } 220 221 return location; 222 223 } 224 225 private Location constructMemberLocation(Member member, String methodName, String memberTypeDesc, String textDescription) 226 { 227 228 ClassNode classNode = readClassNode(member.getDeclaringClass()); 229 230 if (classNode == null) 231 { 232 throw new RuntimeException(String.format("Unable to read class file for %s (to gather line number information).", 233 textDescription)); 234 } 235 236 for (MethodNode mn : (List<MethodNode>) classNode.methods) 237 { 238 if (mn.name.equals(methodName) && mn.desc.equals(memberTypeDesc)) 239 { 240 int lineNumber = findFirstLineNumber(mn.instructions); 241 242 // If debugging info is not available, we may lose the line number data, in which case, 243 // just generate the Location from the textDescription. 244 245 if (lineNumber < 1) 246 { 247 break; 248 } 249 250 String description = String.format("%s (at %s:%d)", textDescription, classNode.sourceFile, lineNumber); 251 252 return new StringLocation(description, lineNumber); 253 } 254 } 255 256 // Didn't find it. Odd. 257 258 return new StringLocation(textDescription, 0); 259 } 260 261 private int findFirstLineNumber(InsnList instructions) 262 { 263 for (AbstractInsnNode node = instructions.getFirst(); node != null; node = node.getNext()) 264 { 265 if (node instanceof LineNumberNode) 266 { 267 return ((LineNumberNode) node).line; 268 } 269 } 270 271 return -1; 272 } 273 274 @Override 275 public void addPlasticClassListener(PlasticClassListener listener) 276 { 277 manager.addPlasticClassListener(listener); 278 } 279 280 @Override 281 public void removePlasticClassListener(PlasticClassListener listener) 282 { 283 manager.removePlasticClassListener(listener); 284 } 285 286}