001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.proxy.compiler;
023:
024: import java.lang.reflect.Method;
025:
026: import org.apache.bcel.Constants;
027: import org.apache.bcel.classfile.JavaClass;
028: import org.apache.bcel.generic.ClassGen;
029: import org.apache.bcel.generic.BasicType;
030: import org.apache.bcel.generic.Type;
031:
032: import org.jboss.logging.Logger;
033:
034: /**
035: * Manages bytecode assembly for dynamic proxy generation.
036: *
037: * @version <tt>$Revision: 57209 $</tt>
038: * @author Unknown
039: * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
040: */
041: public class ProxyCompiler {
042: /** Class logger. */
043: private static final Logger log = Logger
044: .getLogger(ProxyCompiler.class);
045:
046: /** The path (if non-null) where generated classes will be dumped for debugging. */
047: public final static String CLASS_DUMP_PATH = System.getProperty(
048: ProxyCompiler.class.getName() + ".dumpPath", null);
049:
050: /** The suffix for proxy implementation classnames. */
051: public final static String IMPL_SUFFIX = "$Proxy";
052:
053: /** The Runtime classloader for the target proxy. */
054: Runtime runtime;
055:
056: /** The superclass of the target proxy. */
057: Class super class;
058:
059: /** The implementing types of the target proxy. */
060: Class targetTypes[];
061:
062: /** The implementing methods of the target proxy. */
063: Method methods[];
064:
065: /** The class of the targret proxy (set by runtime). */
066: Class proxyType;
067:
068: /**
069: * Creates a new <code>ProxyCompiler</code> instance.
070: *
071: * @param parent a <code>ClassLoader</code> value
072: * @param superclass a <code>Class</code> value
073: * @param targetTypes a <code>Class</code> value
074: * @param methods a <code>Method</code> value
075: */
076: public ProxyCompiler(final ClassLoader parent,
077: final Class super class, final Class targetTypes[],
078: final Method methods[]) throws Exception {
079: this .super class = super class;
080: this .targetTypes = targetTypes;
081: this .methods = methods;
082:
083: this .runtime = new Runtime(parent);
084: this .runtime.targetTypes = targetTypes;
085: this .runtime.methods = methods;
086:
087: runtime.makeProxyType(this );
088: }
089:
090: public Class getProxyType() {
091: return proxyType;
092: }
093:
094: public String getProxyClassName() {
095: // Note: We could reasonably put the $Impl class in either
096: // of two packges: The package of Proxies, or the same package
097: // as the target type. We choose to put it in same package as
098: // the target type, to avoid name encoding issues.
099: //
100: // Note that all infrastructure must be public, because the
101: // $Impl class is inside a different class loader.
102:
103: return targetTypes[0].getName() + IMPL_SUFFIX;
104: }
105:
106: /**
107: * Create the implementation class for the given target.
108: *
109: * @return a <code>byte[]</code> value
110: */
111: public byte[] getCode() {
112: boolean trace = log.isTraceEnabled();
113:
114: final String proxyClassName = getProxyClassName();
115: final String super ClassName = super class.getName();
116:
117: int icount = 1; // don't forget ProxyTarget
118: for (int i = 0; i < targetTypes.length; i++) {
119: Class targetType = targetTypes[i];
120: if (targetType.isInterface()) {
121: icount++;
122: }
123: }
124:
125: String interfaceNames[] = new String[icount];
126: interfaceNames[0] = Proxies.ProxyTarget.class.getName();
127: icount = 1;
128: for (int i = 0; i < targetTypes.length; i++) {
129: Class targetType = targetTypes[i];
130: if (targetType.isInterface()) {
131: interfaceNames[icount++] = targetType.getName();
132: } else if (!super class.isAssignableFrom(targetType)) {
133: throw new RuntimeException("unexpected: " + targetType);
134: }
135: }
136:
137: ClassGen cg = new ClassGen(proxyClassName, super ClassName,
138: "<generated>", Constants.ACC_PUBLIC
139: | Constants.ACC_FINAL, interfaceNames);
140:
141: ProxyImplementationFactory factory = new ProxyImplementationFactory(
142: super ClassName, proxyClassName, cg);
143:
144: cg.addField(factory.createInvocationHandlerField());
145: cg.addField(factory.createRuntimeField());
146: cg.addMethod(factory.createConstructor());
147:
148: // ProxyTarget implementation
149:
150: cg.addMethod(factory.createGetInvocationHandler());
151: cg.addMethod(factory.createGetTargetTypes());
152:
153: boolean haveToString = false;
154:
155: if (trace)
156: log.trace("Creating proxy methods...");
157:
158: // Implement the methods of the target types.
159: for (int i = 0; i < methods.length; i++) {
160: Method m = methods[i];
161: if (trace)
162: log.trace("Reflected method: " + m);
163:
164: String name = m.getName();
165: Class rTypeClass = m.getReturnType();
166: String rTypeName = rTypeClass.getName();
167: Type rType = Utility.getType(rTypeClass);
168: Type[] pTypes = Utility.getTypes(m.getParameterTypes());
169: String[] exceptionNames = getNames(m.getExceptionTypes());
170:
171: if (name.equals("toString") && pTypes.length == 0) {
172: haveToString = true;
173: }
174:
175: org.apache.bcel.classfile.Method proxyMethod = factory
176: .createProxyMethod(name, i, rType, pTypes,
177: exceptionNames);
178:
179: if (trace)
180: log.trace("Created proxy method: " + proxyMethod);
181:
182: cg.addMethod(proxyMethod);
183: }
184:
185: if (!haveToString) {
186: cg.addMethod(factory.createToString());
187: }
188:
189: JavaClass jclass = cg.getJavaClass();
190: if (trace)
191: log.trace("Generated Java class: " + jclass);
192:
193: // dump the class if we have been configured todo so
194: if (CLASS_DUMP_PATH != null) {
195: try {
196: String filename = CLASS_DUMP_PATH
197: + java.io.File.separator + proxyClassName
198: + ".class";
199: log
200: .info("Dumping generated proxy class to "
201: + filename);
202: jclass.dump(filename);
203: } catch (Exception e) {
204: log.error("Failed to dump class file", e);
205: }
206: }
207:
208: return jclass.getBytes();
209: }
210:
211: /**
212: * Returns an array of class names for the given array
213: * of classes.
214: */
215: private String[] getNames(Class[] classes) {
216: String[] names = new String[classes.length];
217: for (int i = 0; i < classes.length; i++) {
218: names[i] = classes[i].getName();
219: }
220:
221: return names;
222: }
223: }
|