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 }