Xpoint
   [напомнить пароль]

Автоматическое создание Proxy-классов

Эта программа автоматически создаёт для любого класса без его исходных текстов прокси-класс с идентичным поведением. Прокси-класс получает в конструкторе на один параметр больше — ClassHandler-объект. Этот объект впоследствии получает уведомления о вызовах методов прокси-класса, их параметры и результат, который вернул исходный класс для этого вызова. Это можно использовать, если нужно узнать, что именно делается с каким-нибудь объектом, который нельзя или нежелательно менять. Этим способом можно, конечно, перехватить только вызовы динамических методов, вызовы статических методов идут мимо прокси-класса.

ProxyFactory.java:

import java.util.*;
import java.io.*;
import java.lang.reflect.*;

public class ProxyFactory
{
    static String typeName(Class c)
    {
        if (c.isArray())
            return typeName(c.getComponentType())+"[]";
        else
            return c.getName();
    }

    public static void createProxyClass(Class c, String newname) throws IOException
    {
        String filename=newname;
        while (filename.indexOf('.')>=0)
            filename = filename.substring(0,filename.indexOf('.'))+"/"+filename.substring(filename.indexOf('.')+1);
        filename += ".java";
    
        String newpackage=null;
        if (newname.lastIndexOf('.')>=0)
        {
            newpackage = newname.substring(0,newname.lastIndexOf('.'));
            newname = newname.substring(newname.lastIndexOf('.')+1);
        }
        
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename)));

        out.write("// This is a proxy class for "+c.getName()+", created by ProxyFactory\n\n");

        if (newpackage!=null)
            out.write("package "+newpackage+";\n\n");

        out.write("import ClassHandler;\n\n");
        out.write("public class "+newname+" extends "+c.getName()+"\n");
        out.write("{\n");
        out.write("\tClassHandler __h = null;\n\n");

        Constructor[] constructors = c.getConstructors();
        for (int i=0;i<constructors.length;i++)
        {
            if (Modifier.isPublic(constructors[i].getModifiers()) && !Modifier.isAbstract(constructors[i].getModifiers()))
            {
                Class[] params = constructors[i].getParameterTypes();
                out.write("\tpublic "+newname+"(");
                for (int j=0;j<params.length;j++)
                {
                    if (j>0)
                        out.write(',');
                    out.write(typeName(params[j])+" p"+j);
                }
                if (params.length>0)
                    out.write(',');
                out.write("ClassHandler h)");
                
                Class[] exceptions = constructors[i].getExceptionTypes();
                if (exceptions.length>0)
                {
                    out.write(" throws ");
                    for (int j=0;j<exceptions.length;j++)
                    {
                        if (j>0)
                            out.write(',');
                        out.write(typeName(exceptions[j]));
                    }
                }
                out.write("\n");
                
                out.write("\t{\n");

                out.write("\t\tsuper(");
                for (int j=0;j<params.length;j++)
                {
                    if (j>0)
                        out.write(',');
                    out.write("p"+j);
                }
                out.write(");\n");
                
                out.write("\t\t__h=h;\n");
                out.write("\t\tObject[] args={");
                for (int j=0;j<params.length;j++)
                {
                    if (j>0)
                        out.write(',');
                    if (params[j].isPrimitive())
                    {
                        if (params[j].getName().equals("int"))
                            out.write("new Integer(p"+j+")");
                        else if (params[j].getName().equals("char"))
                            out.write("new Character(p"+j+")");
                        else
                        {
                            String s=Character.toUpperCase(params[j].getName().charAt(0))+params[j].getName().substring(1);
                            out.write("new "+s+"(p"+j+")");
                        }
                    }
                    else
                        out.write("p"+j);
                }
                out.write("};\n");
                
                out.write("\t\t__h.invokedConstructor(this,args);\n");

                out.write("\t}\n\n");
            }
        }
        
        Method[] methods = c.getMethods();
        for (int i=0;i<methods.length;i++)
        {
            if (Modifier.isPublic(methods[i].getModifiers()) && !Modifier.isAbstract(methods[i].getModifiers()) && !Modifier.isStatic(methods[i].getModifiers()) && !Modifier.isFinal(methods[i].getModifiers()))
            {
                Class[] params = methods[i].getParameterTypes();
                Class returnType = methods[i].getReturnType();
                out.write("\tpublic "+typeName(returnType)+" "+methods[i].getName()+"(");
                for (int j=0;j<params.length;j++)
                {
                    if (j>0)
                        out.write(',');
                    out.write(typeName(params[j])+" p"+j);
                }
                out.write(")");
                
                Class[] exceptions = methods[i].getExceptionTypes();
                if (exceptions.length>0)
                {
                    out.write(" throws ");
                    for (int j=0;j<exceptions.length;j++)
                    {
                        if (j>0)
                            out.write(',');
                        out.write(typeName(exceptions[j]));
                    }
                }
                out.write("\n");
                
                out.write("\t{\n");

                out.write("\t\t");
                if (!returnType.getName().equals("void"))
                    out.write(typeName(returnType)+" ret = ");
                out.write("super."+methods[i].getName()+"(");
                for (int j=0;j<params.length;j++)
                {
                    if (j>0)
                        out.write(',');
                    out.write("p"+j);
                }
                out.write(");\n");
                
                out.write("\t\tObject[] args={");
                for (int j=0;j<params.length;j++)
                {
                    if (j>0)
                        out.write(',');
                    if (params[j].isPrimitive())
                    {
                        if (params[j].getName().equals("int"))
                            out.write("new Integer(p"+j+")");
                        else if (params[j].getName().equals("char"))
                            out.write("new Character(p"+j+")");
                        else
                        {
                            String s=Character.toUpperCase(params[j].getName().charAt(0))+params[j].getName().substring(1);
                            out.write("new "+s+"(p"+j+")");
                        }
                    }
                    else
                        out.write("p"+j);
                }
                out.write("};\n");
                
                out.write("\t\tif (__h != null)\n");
                out.write("\t\t\t__h.invokedMethod(this,\""+methods[i].getName()+"\",args,");
                if (returnType.isPrimitive())
                {
                    if (returnType.getName().equals("void"))
                        out.write("null");
                    else if (returnType.getName().equals("int"))
                        out.write("new Integer(ret)");
                    else if (returnType.getName().equals("char"))
                        out.write("new Character(ret)");
                    else
                    {
                        String s=Character.toUpperCase(returnType.getName().charAt(0))+returnType.getName().substring(1);
                        out.write("new "+s+"(ret)");
                    }
                }
                else
                    out.write("ret");

                out.write(");\n");
                if (!returnType.getName().equals("void"))
                    out.write("\t\treturn ret;\n");

                out.write("\t}\n\n");
            }
        }

        out.write("}\n");
        out.close();
    }

    public static void main(String[] args) throws Throwable
    {
        if (args.length<2)
        {
            System.err.println("Usage: java ProxyFactory class-name-old class-name-new");
            System.err.println();
            System.exit(1);
        }
        
        createProxyClass(Class.forName(args[0]), args[1]);
    }
}

ClassHandler.java:

import java.lang.reflect.*;

public interface ClassHandler
{
    void invokedConstructor(Object proxy, Object[] args);
    void invokedMethod(Object proxy, String method, Object[] args, Object returnValue);
}
Powered by POEM™ Engine Copyright © 2002-2005