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