/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package org.googlecode.perftrace.javassist.tools.rmi;
import java.lang.reflect.Method;
import java.util.Hashtable;
import org.googlecode.perftrace.javassist.*;
import org.googlecode.perftrace.javassist.CtMethod.ConstParameter;
/**
* A stub-code generator. It is used for producing a proxy class.
*
* <p>The proxy class for class A is as follows:
*
* <ul><pre>public class A implements Proxy, Serializable {
* private ObjectImporter importer;
* private int objectId;
* public int _getObjectId() { return objectId; }
* public A(ObjectImporter oi, int id) {
* importer = oi; objectId = id;
* }
*
* ... the same methods that the original class A declares ...
* }</pre></ul>
*
* <p>Instances of the proxy class is created by an
* <code>ObjectImporter</code> object.
*/
public class StubGenerator implements Translator {
private static final String fieldImporter = "importer";
private static final String fieldObjectId = "objectId";
private static final String accessorObjectId = "_getObjectId";
private static final String sampleClass = "javassist.tools.rmi.Sample";
private ClassPool classPool;
private Hashtable proxyClasses;
private CtMethod forwardMethod;
private CtMethod forwardStaticMethod;
private CtClass[] proxyConstructorParamTypes;
private CtClass[] interfacesForProxy;
private CtClass[] exceptionForProxy;
/**
* Constructs a stub-code generator.
*/
public StubGenerator() {
proxyClasses = new Hashtable();
}
/**
* Initializes the object.
* This is a method declared in javassist.Translator.
*
* @see org.googlecode.perftrace.javassist.Translator#start(ClassPool)
*/
public void start(ClassPool pool) throws NotFoundException {
classPool = pool;
CtClass c = pool.get(sampleClass);
forwardMethod = c.getDeclaredMethod("forward");
forwardStaticMethod = c.getDeclaredMethod("forwardStatic");
proxyConstructorParamTypes
= pool.get(new String[] { "javassist.tools.rmi.ObjectImporter",
"int" });
interfacesForProxy
= pool.get(new String[] { "java.io.Serializable",
"javassist.tools.rmi.Proxy" });
exceptionForProxy
= new CtClass[] { pool.get("javassist.tools.rmi.RemoteException") };
}
/**
* Does nothing.
* This is a method declared in javassist.Translator.
* @see org.googlecode.perftrace.javassist.Translator#onLoad(ClassPool,String)
*/
public void onLoad(ClassPool pool, String classname) {}
/**
* Returns <code>true</code> if the specified class is a proxy class
* recorded by <code>makeProxyClass()</code>.
*
* @param name a fully-qualified class name
*/
public boolean isProxyClass(String name) {
return proxyClasses.get(name) != null;
}
/**
* Makes a proxy class. The produced class is substituted
* for the original class.
*
* @param clazz the class referenced
* through the proxy class.
* @return <code>false</code> if the proxy class
* has been already produced.
*/
public synchronized boolean makeProxyClass(Class clazz)
throws CannotCompileException, NotFoundException
{
String classname = clazz.getName();
if (proxyClasses.get(classname) != null)
return false;
else {
CtClass ctclazz = produceProxyClass(classPool.get(classname),
clazz);
proxyClasses.put(classname, ctclazz);
modifySuperclass(ctclazz);
return true;
}
}
private CtClass produceProxyClass(CtClass orgclass, Class orgRtClass)
throws CannotCompileException, NotFoundException
{
int modify = orgclass.getModifiers();
if (Modifier.isAbstract(modify) || Modifier.isNative(modify)
|| !Modifier.isPublic(modify))
throw new CannotCompileException(orgclass.getName()
+ " must be public, non-native, and non-abstract.");
CtClass proxy = classPool.makeClass(orgclass.getName(),
orgclass.getSuperclass());
proxy.setInterfaces(interfacesForProxy);
CtField f
= new CtField(classPool.get("javassist.tools.rmi.ObjectImporter"),
fieldImporter, proxy);
f.setModifiers(Modifier.PRIVATE);
proxy.addField(f, CtField.Initializer.byParameter(0));
f = new CtField(CtClass.intType, fieldObjectId, proxy);
f.setModifiers(Modifier.PRIVATE);
proxy.addField(f, CtField.Initializer.byParameter(1));
proxy.addMethod(CtNewMethod.getter(accessorObjectId, f));
proxy.addConstructor(CtNewConstructor.defaultConstructor(proxy));
CtConstructor cons
= CtNewConstructor.skeleton(proxyConstructorParamTypes,
null, proxy);
proxy.addConstructor(cons);
try {
addMethods(proxy, orgRtClass.getMethods());
return proxy;
}
catch (SecurityException e) {
throw new CannotCompileException(e);
}
}
private CtClass toCtClass(Class rtclass) throws NotFoundException {
String name;
if (!rtclass.isArray())
name = rtclass.getName();
else {
StringBuffer sbuf = new StringBuffer();
do {
sbuf.append("[]");
rtclass = rtclass.getComponentType();
} while(rtclass.isArray());
sbuf.insert(0, rtclass.getName());
name = sbuf.toString();
}
return classPool.get(name);
}
private CtClass[] toCtClass(Class[] rtclasses) throws NotFoundException {
int n = rtclasses.length;
CtClass[] ctclasses = new CtClass[n];
for (int i = 0; i < n; ++i)
ctclasses[i] = toCtClass(rtclasses[i]);
return ctclasses;
}
/* ms must not be an array of CtMethod. To invoke a method ms[i]
* on a server, a client must send i to the server.
*/
private void addMethods(CtClass proxy, Method[] ms)
throws CannotCompileException, NotFoundException
{
CtMethod wmethod;
for (int i = 0; i < ms.length; ++i) {
Method m = ms[i];
int mod = m.getModifiers();
if (m.getDeclaringClass() != Object.class
&& !Modifier.isFinal(mod))
if (Modifier.isPublic(mod)) {
CtMethod body;
if (Modifier.isStatic(mod))
body = forwardStaticMethod;
else
body = forwardMethod;
wmethod
= CtNewMethod.wrapped(toCtClass(m.getReturnType()),
m.getName(),
toCtClass(m.getParameterTypes()),
exceptionForProxy,
body,
ConstParameter.integer(i),
proxy);
wmethod.setModifiers(mod);
proxy.addMethod(wmethod);
}
else if (!Modifier.isProtected(mod)
&& !Modifier.isPrivate(mod))
// if package method
throw new CannotCompileException(
"the methods must be public, protected, or private.");
}
}
/**
* Adds a default constructor to the super classes.
*/
private void modifySuperclass(CtClass orgclass)
throws CannotCompileException, NotFoundException
{
CtClass superclazz;
for (;; orgclass = superclazz) {
superclazz = orgclass.getSuperclass();
if (superclazz == null)
break;
try {
superclazz.getDeclaredConstructor(null);
break; // the constructor with no arguments is found.
}
catch (NotFoundException e) {
}
superclazz.addConstructor(
CtNewConstructor.defaultConstructor(superclazz));
}
}
}