/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 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. * * 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 javassist.util.proxy; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Modifier; import java.security.ProtectionDomain; import java.util.*; import java.lang.ref.WeakReference; import javassist.CannotCompileException; import javassist.bytecode.*; /* * This class is implemented only with the lower-level API of Javassist. * This design decision is for maximizing performance. */ /** * Factory of dynamic proxy classes. * *
This factory generates a class that extends the given super class and implements
* the given interfaces. The calls of the methods inherited from the super class are
* forwarded and then invoke()
is called on the method handler
* associated with instances of the generated class. The calls of the methods from
* the interfaces are also forwarded to the method handler.
*
*
For example, if the following code is executed, * *
* ProxyFactory f = new ProxyFactory(); * f.setSuperclass(Foo.class); * f.setFilter(new MethodFilter() { * public boolean isHandled(Method m) { * // ignore finalize() * return !m.getName().equals("finalize"); * } * }); * Class c = f.createClass(); * MethodHandler mi = new MethodHandler() { * public Object invoke(Object self, Method m, Method proceed, * Object[] args) throws Throwable { * System.out.println("Name: " + m.getName()); * return proceed.invoke(self, args); // execute the original method. * } * }; * Foo foo = (Foo)c.newInstance(); * ((ProxyObject)foo).setHandler(mi); ** *
Then, the following method call will be forwarded to MethodHandler
* mi
and prints a message before executing the originally called method
* bar()
in Foo
.
*
*
* foo.bar(); ** *
The last three lines of the code shown above can be replaced with a call to
* the helper method create
, which generates a proxy class, instantiates
* it, and sets the method handler of the instance:
*
*
* : * Foo foo = (Foo)f.create(new Class[0], new Object[0], mi); ** *
To change the method handler during runtime, * execute the following code: * *
* MethodHandler mi = ... ; // alternative handler * ((ProxyObject)foo).setHandler(mi); ** *
If setHandler is never called for a proxy instance then it will * employ the default handler which proceeds by invoking the original method. * The behaviour of the default handler is identical to the following * handler: * *
* class EmptyHandler implements MethodHandler { * public Object invoke(Object self, Method m, * Method proceed, Object[] args) throws Exception { * return proceed.invoke(self, args); * } * } ** *
A proxy factory caches and reuses proxy classes by default. It is possible to reset * this default globally by setting static field {@link ProxyFactory#useCache} to false. * Caching may also be configured for a specific factory by calling instance method * {@link ProxyFactory#setUseCache(boolean)}. It is strongly recommended that new clients * of class ProxyFactory enable caching. Failure to do so may lead to exhaustion of * the heap memory area used to store classes. * *
Caching is automatically disabled for any given proxy factory if deprecated instance * method {@link ProxyFactory#setHandler(MethodHandler)} is called. This method was * used to specify a default handler which newly created proxy classes should install * when they create their instances. It is only retained to provide backward compatibility * with previous releases of javassist. Unfortunately,this legacy behaviour makes caching * and reuse of proxy classes impossible. The current programming model expects javassist * clients to set the handler of a proxy instance explicitly by calling method * {@link ProxyObject#setHandler(MethodHandler)} as shown in the sample code above. New * clients are strongly recommended to use this model rather than calling * {@link ProxyFactory#setHandler(MethodHandler)}. * *
A proxy object generated by ProxyFactory
is serializable
* if its super class or any of its interfaces implement java.io.Serializable
.
* However, a serialized proxy object may not be compatible with future releases.
* The serialization support should be used for short-term storage or RMI.
*
*
For compatibility with older releases serialization of proxy objects is implemented by * adding a writeReplace method to the proxy class. This allows a proxy to be serialized * to a conventional {@link java.io.ObjectOutputStream} and deserialized from a corresponding * {@link java.io.ObjectInputStream}. However this method suffers from several problems, the most * notable one being that it fails to serialize state inherited from the proxy's superclass. *
* An alternative method of serializing proxy objects is available which fixes these problems. It
* requires inhibiting generation of the writeReplace method and instead using instances of
* {@link javassist.util.proxy.ProxyObjectOutputStream} and {@link javassist.util.proxy.ProxyObjectInputStream}
* (which are subclasses of {@link java.io.ObjectOutputStream} and {@link java.io.ObjectInputStream})
* to serialize and deserialize, respectively, the proxy. These streams recognise javassist proxies and ensure
* that they are serialized and deserialized without the need for the proxy class to implement special methods
* such as writeReplace. Generation of the writeReplace method can be disabled globally by setting static field
* {@link ProxyFactory#useWriteReplace} to false. Alternatively, it may be
* configured per factory by calling instance method {@link ProxyFactory#setUseWriteReplace(boolean)}.
*
* @see MethodHandler
* @since 3.1
* @author Muga Nishizawa
* @author Shigeru Chiba
* @author Andrew Dinn
*/
public class ProxyFactory {
private Class superClass;
private Class[] interfaces;
private MethodFilter methodFilter;
private MethodHandler handler; // retained for legacy usage
private List signatureMethods;
private byte[] signature;
private String classname;
private String basename;
private String superName;
private Class thisClass;
/**
* per factory setting initialised from current setting for useCache but able to be reset before each create call
*/
private boolean factoryUseCache;
/**
* per factory setting initialised from current setting for useWriteReplace but able to be reset before each create call
*/
private boolean factoryWriteReplace;
/**
* If the value of this variable is not null, the class file of
* the generated proxy class is written under the directory specified
* by this variable. For example, if the value is
* "."
, then the class file is written under the current
* directory. This method is for debugging.
*
*
The default value is null.
*/
public String writeDirectory;
private static final Class OBJECT_TYPE = Object.class;
private static final String HOLDER = "_methods_";
private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;";
private static final String FILTER_SIGNATURE_FIELD = "_filter_signature";
private static final String FILTER_SIGNATURE_TYPE = "[B";
private static final String HANDLER = "handler";
private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport";
private static final String DEFAULT_INTERCEPTOR = "default_interceptor";
private static final String HANDLER_TYPE
= 'L' + MethodHandler.class.getName().replace('.', '/') + ';';
private static final String HANDLER_SETTER = "setHandler";
private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V";
private static final String HANDLER_GETTER = "getHandler";
private static final String HANDLER_GETTER_TYPE = "()" + HANDLER_TYPE;
private static final String SERIAL_VERSION_UID_FIELD = "serialVersionUID";
private static final String SERIAL_VERSION_UID_TYPE = "J";
private static final int SERIAL_VERSION_UID_VALUE = -1;
/**
* If true, a generated proxy class is cached and it will be reused
* when generating the proxy class with the same properties is requested.
* The default value is true.
*
* Note that this value merely specifies the initial setting employed by any newly created
* proxy factory. The factory setting may be overwritten by calling factory instance method
* {@link #setUseCache(boolean)}
*
* @since 3.4
*/
public static volatile boolean useCache = true;
/**
* If true, a generated proxy class will implement method writeReplace enabling
* serialization of its proxies to a conventional ObjectOutputStream. this (default)
* setting retains the old javassist behaviour which has the advantage that it
* retains compatibility with older releases and requires no extra work on the part
* of the client performing the serialization. However, it has the disadvantage that
* state inherited from the superclasses of the proxy is lost during serialization.
* if false then serialization/deserialization of the proxy instances will preserve
* all fields. However, serialization must be performed via a {@link ProxyObjectOutputStream}
* and deserialization must be via {@link ProxyObjectInputStream}. Any attempt to serialize
* proxies whose class was created with useWriteReplace set to false via a normal
* {@link java.io.ObjectOutputStream} will fail.
*
* Note that this value merely specifies the initial setting employed by any newly created
* proxy factory. The factory setting may be overwritten by calling factory instance method
* {@link #setUseWriteReplace(boolean)}
*
* @since 3.4
*/
public static volatile boolean useWriteReplace = true;
/*
* methods allowing individual factory settings for factoryUseCache and factoryWriteReplace to be reset
*/
/**
* test whether this factory uses the proxy cache
* @return true if this factory uses the proxy cache otherwise false
*/
public boolean isUseCache()
{
return factoryUseCache;
}
/**
* configure whether this factory should use the proxy cache
* @param useCache true if this factory should use the proxy cache and false if it should not use the cache
* @throws RuntimeException if a default interceptor has been set for the factory
*/
public void setUseCache(boolean useCache)
{
// we cannot allow caching to be used if the factory is configured to install a default interceptor
// field into generated classes
if (handler != null && useCache) {
throw new RuntimeException("caching cannot be enabled if the factory default interceptor has been set");
}
factoryUseCache = useCache;
}
/**
* test whether this factory installs a writeReplace method in created classes
* @return true if this factory installs a writeReplace method in created classes otherwise false
*/
public boolean isUseWriteReplace()
{
return factoryWriteReplace;
}
/**
* configure whether this factory should add a writeReplace method to created classes
* @param useWriteReplace true if this factory should add a writeReplace method to created classes and false if it
* should not add a writeReplace method
*/
public void setUseWriteReplace(boolean useWriteReplace)
{
factoryWriteReplace = useWriteReplace;
}
private static WeakHashMap proxyCache = new WeakHashMap();
/**
* determine if a class is a javassist proxy class
* @param cl
* @return true if the class is a javassist proxy class otherwise false
*/
public static boolean isProxyClass(Class cl)
{
// all proxies implement ProxyObject. nothing else should.
return (ProxyObject.class.isAssignableFrom(cl));
}
/**
* used to store details of a specific proxy class in the second tier of the proxy cache. this entry
* will be located in a hashmap keyed by the unique identifying name of the proxy class. the hashmap is
* located in a weak hashmap keyed by the classloader common to all proxy classes in the second tier map.
*/
static class ProxyDetails {
/**
* the unique signature of any method filter whose behaviour will be met by this class. each bit in
* the byte array is set if the filter redirects the corresponding super or interface method and clear
* if it does not redirect it.
*/
byte[] signature;
/**
* a hexadecimal string representation of the signature bit sequence. this string also forms part
* of the proxy class name.
*/
WeakReference proxyClass;
/**
* a flag which is true this class employs writeReplace to perform serialization of its instances
* and false if serialization must employ of a ProxyObjectOutputStream and ProxyObjectInputStream
*/
boolean isUseWriteReplace;
ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace)
{
this.signature = signature;
this.proxyClass = new WeakReference(proxyClass);
this.isUseWriteReplace = isUseWriteReplace;
}
}
/**
* Constructs a factory of proxy class.
*/
public ProxyFactory() {
superClass = null;
interfaces = null;
methodFilter = null;
handler = null;
signature = null;
signatureMethods = null;
thisClass = null;
writeDirectory = null;
factoryUseCache = useCache;
factoryWriteReplace = useWriteReplace;
}
/**
* Sets the super class of a proxy class.
*/
public void setSuperclass(Class clazz) {
superClass = clazz;
// force recompute of signature
signature = null;
}
/**
* Obtains the super class set by setSuperclass()
.
*
* @since 3.4
*/
public Class getSuperclass() { return superClass; }
/**
* Sets the interfaces of a proxy class.
*/
public void setInterfaces(Class[] ifs) {
interfaces = ifs;
// force recompute of signature
signature = null;
}
/**
* Obtains the interfaces set by setInterfaces
.
*
* @since 3.4
*/
public Class[] getInterfaces() { return interfaces; }
/**
* Sets a filter that selects the methods that will be controlled by a handler.
*/
public void setFilter(MethodFilter mf) {
methodFilter = mf;
// force recompute of signature
signature = null;
}
/**
* Generates a proxy class using the current filter.
*/
public Class createClass() {
if (signature == null) {
computeSignature(methodFilter);
}
return createClass1();
}
/**
* Generates a proxy class using the supplied filter.
*/
public Class createClass(MethodFilter filter) {
computeSignature(filter);
return createClass1();
}
/**
* Generates a proxy class with a specific signature.
* access is package local so ProxyObjectInputStream can use this
* @param signature
* @return
*/
Class createClass(byte[] signature)
{
installSignature(signature);
return createClass1();
}
private Class createClass1() {
if (thisClass == null) {
ClassLoader cl = getClassLoader();
synchronized (proxyCache) {
if (factoryUseCache)
createClass2(cl);
else
createClass3(cl);
}
}
// don't retain any unwanted references
Class result = thisClass;
thisClass = null;
return result;
}
private static char[] hexDigits =
{ '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
public String getKey(Class superClass, Class[] interfaces, byte[] signature, boolean useWriteReplace)
{
StringBuffer sbuf = new StringBuffer();
if (superClass != null){
sbuf.append(superClass.getName());
}
sbuf.append(":");
for (int i = 0; i < interfaces.length; i++) {
sbuf.append(interfaces[i].getName());
sbuf.append(":");
}
for (int i = 0; i < signature.length; i++) {
byte b = signature[i];
int lo = b & 0xf;
int hi = (b >> 4) & 0xf;
sbuf.append(hexDigits[lo]);
sbuf.append(hexDigits[hi]);
}
if (useWriteReplace) {
sbuf.append(":w");
}
return sbuf.toString();
}
private void createClass2(ClassLoader cl) {
String key = getKey(superClass, interfaces, signature, factoryWriteReplace);
/*
* Excessive concurrency causes a large memory footprint and slows the
* execution speed down (with JDK 1.5). Thus, we use a jumbo lock for
* reducing concrrency.
*/
// synchronized (proxyCache) {
HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl);
ProxyDetails details;
if (cacheForTheLoader == null) {
cacheForTheLoader = new HashMap();
proxyCache.put(cl, cacheForTheLoader);
}
details = (ProxyDetails)cacheForTheLoader.get(key);
if (details != null) {
WeakReference reference = details.proxyClass;
thisClass = (Class)reference.get();
if (thisClass != null) {
return;
}
}
createClass3(cl);
details = new ProxyDetails(signature, thisClass, factoryWriteReplace);
cacheForTheLoader.put(key, details);
// }
}
private void createClass3(ClassLoader cl) {
// we need a new class so we need a new class name
allocateClassName();
try {
ClassFile cf = make();
if (writeDirectory != null)
FactoryHelper.writeFile(cf, writeDirectory);
thisClass = FactoryHelper.toClass(cf, cl, getDomain());
setField(FILTER_SIGNATURE_FIELD, signature);
// legacy behaviour : we only set the default interceptor static field if we are not using the cache
if (!factoryUseCache) {
setField(DEFAULT_INTERCEPTOR, handler);
}
}
catch (CannotCompileException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private void setField(String fieldName, Object value) {
if (thisClass != null && value != null)
try {
Field f = thisClass.getField(fieldName);
SecurityActions.setAccessible(f, true);
f.set(null, value);
SecurityActions.setAccessible(f, false);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
static byte[] getFilterSignature(Class clazz) {
return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD);
}
private static Object getField(Class clazz, String fieldName) {
try {
Field f = clazz.getField(fieldName);
f.setAccessible(true);
Object value = f.get(null);
f.setAccessible(false);
return value;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* A provider of class loaders.
*
* @see #classLoaderProvider
* @since 3.4
*/
public static interface ClassLoaderProvider {
/**
* Returns a class loader.
*
* @param pf a proxy factory that is going to obtain a class loader.
*/
public ClassLoader get(ProxyFactory pf);
}
/**
* A provider used by createClass()
for obtaining
* a class loader.
* get()
on this ClassLoaderProvider
object
* is called to obtain a class loader.
*
*
The value of this field can be updated for changing the default * implementation. * *
Example: *
* ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() { * public ClassLoader get(ProxyFactory pf) { * return Thread.currentThread().getContextClassLoader(); * } * }; ** * @since 3.4 */ public static ClassLoaderProvider classLoaderProvider = new ClassLoaderProvider() { public ClassLoader get(ProxyFactory pf) { return pf.getClassLoader0(); } }; protected ClassLoader getClassLoader() { return classLoaderProvider.get(this); } protected ClassLoader getClassLoader0() { ClassLoader loader = null; if (superClass != null && !superClass.getName().equals("java.lang.Object")) loader = superClass.getClassLoader(); else if (interfaces != null && interfaces.length > 0) loader = interfaces[0].getClassLoader(); if (loader == null) { loader = getClass().getClassLoader(); // In case javassist is in the endorsed dir if (loader == null) { loader = Thread.currentThread().getContextClassLoader(); if (loader == null) loader = ClassLoader.getSystemClassLoader(); } } return loader; } protected ProtectionDomain getDomain() { Class clazz; if (superClass != null && !superClass.getName().equals("java.lang.Object")) clazz = superClass; else if (interfaces != null && interfaces.length > 0) clazz = interfaces[0]; else clazz = this.getClass(); return clazz.getProtectionDomain(); } /** * Creates a proxy class and returns an instance of that class. * * @param paramTypes parameter types for a constructor. * @param args arguments passed to a constructor. * @param mh the method handler for the proxy class. * @since 3.4 */ public Object create(Class[] paramTypes, Object[] args, MethodHandler mh) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { Object obj = create(paramTypes, args); ((ProxyObject)obj).setHandler(mh); return obj; } /** * Creates a proxy class and returns an instance of that class. * * @param paramTypes parameter types for a constructor. * @param args arguments passed to a constructor. */ public Object create(Class[] paramTypes, Object[] args) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { Class c = createClass(); Constructor cons = c.getConstructor(paramTypes); return cons.newInstance(args); } /** * Sets the default invocation handler. This invocation handler is shared * among all the instances of a proxy class unless another is explicitly * specified. * @deprecated since 3.12 * use of this method is incompatible with proxy class caching. * instead clients should call method {@link ProxyObject#setHandler(MethodHandler)} to set the handler * for each newly created proxy instance. * calling this method will automatically disable caching of classes created by the proxy factory. */ public void setHandler(MethodHandler mi) { // if we were using the cache and the handler is non-null then we must stop caching if (factoryUseCache && mi != null) { factoryUseCache = false; // clear any currently held class so we don't try to reuse it or set its handler field thisClass = null; } handler = mi; // this retains the behaviour of the old code which resets any class we were holding on to // this is probably not what is wanted setField(DEFAULT_INTERCEPTOR, handler); } private static int counter = 0; private static synchronized String makeProxyName(String classname) { return classname + "_$$_javassist_" + counter++; } private ClassFile make() throws CannotCompileException { ClassFile cf = new ClassFile(false, classname, superName); cf.setAccessFlags(AccessFlag.PUBLIC); setInterfaces(cf, interfaces); ConstPool pool = cf.getConstPool(); // legacy: we only add the static field for the default interceptor if caching is disabled if (!factoryUseCache) { FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE); finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); cf.addField(finfo); } // handler is per instance FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE); finfo2.setAccessFlags(AccessFlag.PRIVATE); cf.addField(finfo2); // filter signature is per class FieldInfo finfo3 = new FieldInfo(pool, FILTER_SIGNATURE_FIELD, FILTER_SIGNATURE_TYPE); finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); cf.addField(finfo3); // the proxy class serial uid must always be a fixed value FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE); finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL); cf.addField(finfo4); // HashMap allMethods = getMethods(superClass, interfaces); // int size = allMethods.size(); makeConstructors(classname, cf, pool, classname); int s = overrideMethods(cf, pool, classname); addMethodsHolder(cf, pool, classname, s); addSetter(classname, cf, pool); addGetter(classname, cf, pool); if (factoryWriteReplace) { try { cf.addMethod(makeWriteReplace(pool)); } catch (DuplicateMemberException e) { // writeReplace() is already declared in the super class/interfaces. } } thisClass = null; return cf; } private void checkClassAndSuperName() { if (interfaces == null) interfaces = new Class[0]; if (superClass == null) { superClass = OBJECT_TYPE; superName = superClass.getName(); basename = interfaces.length == 0 ? superName : interfaces[0].getName(); } else { superName = superClass.getName(); basename = superName; } if (Modifier.isFinal(superClass.getModifiers())) throw new RuntimeException(superName + " is final"); if (basename.startsWith("java.")) basename = "org.javassist.tmp." + basename; } private void allocateClassName() { classname = makeProxyName(basename); } private static Comparator sorter = new Comparator() { public int compare(Object o1, Object o2) { Map.Entry e1 = (Map.Entry)o1; Map.Entry e2 = (Map.Entry)o2; String key1 = (String)e1.getKey(); String key2 = (String)e2.getKey(); return key1.compareTo(key2); } }; private void makeSortedMethodList() { checkClassAndSuperName(); HashMap allMethods = getMethods(superClass, interfaces); signatureMethods = new ArrayList(allMethods.entrySet()); Collections.sort(signatureMethods, sorter); } private void computeSignature(MethodFilter filter) // throws CannotCompileException { makeSortedMethodList(); int l = signatureMethods.size(); int maxBytes = ((l + 7) >> 3); signature = new byte[maxBytes]; for (int idx = 0; idx < l; idx++) { Map.Entry e = (Map.Entry)signatureMethods.get(idx); Method m = (Method)e.getValue(); int mod = m.getModifiers(); if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod) && isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) { setBit(signature, idx); } } } private void installSignature(byte[] signature) // throws CannotCompileException { makeSortedMethodList(); int l = signatureMethods.size(); int maxBytes = ((l + 7) >> 3); if (signature.length != maxBytes) { throw new RuntimeException("invalid filter signature length for deserialized proxy class"); } this.signature = signature; } private boolean testBit(byte[] signature, int idx) { int byteIdx = idx >> 3; if (byteIdx > signature.length) { return false; } else { int bitIdx = idx & 0x7; int mask = 0x1 << bitIdx; int sigByte = signature[byteIdx]; return ((sigByte & mask) != 0); } } private void setBit(byte[] signature, int idx) { int byteIdx = idx >> 3; if (byteIdx < signature.length) { int bitIdx = idx & 0x7; int mask = 0x1 << bitIdx; int sigByte = signature[byteIdx]; signature[byteIdx] = (byte)(sigByte | mask); } } private static void setInterfaces(ClassFile cf, Class[] interfaces) { String setterIntf = ProxyObject.class.getName(); String[] list; if (interfaces == null || interfaces.length == 0) list = new String[] { setterIntf }; else { list = new String[interfaces.length + 1]; for (int i = 0; i < interfaces.length; i++) list[i] = interfaces[i].getName(); list[interfaces.length] = setterIntf; } cf.setInterfaces(list); } private static void addMethodsHolder(ClassFile cf, ConstPool cp, String classname, int size) throws CannotCompileException { FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE); finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC); cf.addField(finfo); MethodInfo minfo = new MethodInfo(cp, "