Исключение при генерации Javaassist во время выполнения POJO - PullRequest
1 голос
/ 28 июня 2019

У меня возникает следующая ошибка при создании POJO:

java.lang.RuntimeException: act_ge_bytearray: frozen class (cannot edit)
    at javassist.ClassPool.checkNotFrozen(ClassPool.java:587) ~[javassist-3.25.0-GA.jar:na]
    at javassist.ClassPool.makeClass(ClassPool.java:829) ~[javassist-3.25.0-GA.jar:na]
    at javassist.ClassPool.makeClass(ClassPool.java:806) ~[javassist-3.25.0-GA.jar:na]

Я создаю POJO следующим образом:

Class<?> clazz = null;
try {
    clazz = PojoGenerator.generate( "net.javaforge.blog.javassist.Pojo$Generated", props);
} catch (NotFoundException e) {
    e.printStackTrace();
} catch (CannotCompileException e) {
    e.printStackTrace();
}

Объект создается после выбора конкретной таблицы.В первой попытке это работает, но любая другая не помогает.

Например, в первой попытке после выбора объекта foo POJO создается хорошо, но после выбора другого элемента появляется проблема.

РЕДАКТИРОВАТЬ

Я создал класс генератора POJO на основе следующего урока: https://blog.javaforge.net/post/31913732423/howto-create-java-pojo-at-runtime-with-javassist

РЕДАКТИРОВАТЬ 2 IЯ использую Vaadin для представления схемы таблиц PostgreSQL в веб-приложении.

Следующий код получает каждый столбец выбранной таблицы и создает properties, getters и setters для заданных имен.Затем я создаю столько объектов, сколько записей в выбранной таблице

package pl.registers.spring.view;

import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import javassist.CannotCompileException;
import javassist.NotFoundException;
import pl.registers.spring.service.DbService;
import pl.registers.spring.test.DatabaseTableModel;
import pojo.PojoGenerator;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Route("")
public class TablesView extends VerticalLayout {
    private List<String> tables;

        public TablesView()  {
        Connection con=null;
        try {
            con = DriverManager.getConnection("jdbc:postgresql://localhost:5432/process-engine", "postgres",
                    "postgres");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        ComboBox tablesCombo = new ComboBox();
        try {
            tables = DbService.getInstance().getTableNames(con);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        Map<String, Class<?>> props = new HashMap<String, Class<?>>();
        List items = new ArrayList();
        Grid grid = new Grid();

            try {
                tablesCombo.setItems(DbService.getInstance().getTableNames(con));
            } catch (SQLException e) {
                e.printStackTrace();
            }

            tablesCombo.addValueChangeListener(table -> {
                items.clear();
            try {
                DatabaseTableModel dP = new DatabaseTableModel(DriverManager.getConnection("jdbc:postgresql://localhost:5432/process-engine", "postgres",
                        "postgres"), table.getValue());

                //get column names
                for(int i = 0; i < dP.getColumnCount(); i++) {
                    props.put(dP.getColumnName(i), String.class);
                }

                //get records
                System.out.println("data vector: " + dP.getDataVector());

                //generate Class schema

                Class<?> clazz = null;

                try {
                    clazz = PojoGenerator.generate(
                            "net.javaforge.blog.javassist.Pojo$Generated", props);
//                            table.getValue().toString(), props);
                } catch (NotFoundException e) {
                    e.printStackTrace();
                } catch (CannotCompileException e) {
                    e.printStackTrace();
                }

                //create as many objects as many records
                try {
                    for(int i = 0; i < dP.getDataVector().size(); i++) {
                        items.add(clazz.newInstance());
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

                List dataVectorCastedToList = dP.getDataVector();

                //set objects values
                for(int i = 0; i <= items.size()-1; i++) {
                    try {
                        clazz.getMethod("Constructor", java.util.List.class).invoke(items.get(i), dataVectorCastedToList.get(i));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                }

                List<String> getters = new ArrayList<>();
                //get methods
                for(int i = 0; i < clazz.getMethods().length-1; i++){
                    String methodName = clazz.getMethods()[i].getName();
                    if(methodName.contains("get")) {
                        getters.add(methodName);
                    }
                }

                //remove getClass method
                if(getters.contains("getClass")) 
     getters.remove("getClass");
                System.out.println("getters" + getters);

                grid.setItems(items);

                //set columns
                getters.forEach(item -> {
                    grid.addColumn(column -> {
                         try {
                            return 
 column.getClass().getMethod(item).invoke(column);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        }
                        return null;
                    }).setHeader(item);
                });

            } catch (SQLException e) {
                e.printStackTrace();
            }
        });

        //add to layout
        add(tablesCombo, grid);
    }
}

Я также создал свой собственный метод generateInitMethod в классе PojoGenerator, который устанавливает данные для созданных объектов.Вот как PojoGenerator выглядит следующим образом:

package pojo;

import javassist.*;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class PojoGenerator {

    public static Class generate(String className, Map<String, Class<?>> properties) throws NotFoundException, CannotCompileException {
        CtClass cc;

        System.out.println("properties passed to pojo generator: " + properties);

        ClassPool pool = ClassPool.getDefault();
        cc = pool.makeClass(className);
        System.out.println("is frozen " + cc.isFrozen());

        cc.debugWriteFile();

        // add this to define a super class to extend
        // cc.setSuperclass(resolveCtClass(MySuperClass.class));

        // add this to define an interface to implement
        cc.addInterface(resolveCtClass(Serializable.class));

        for (Map.Entry<String, Class<?>> entry : properties.entrySet()) {

            cc.addField(new CtField(resolveCtClass(entry.getValue()), entry.getKey(), cc));

            // add getter
            cc.addMethod(generateGetter(cc, entry.getKey(), entry.getValue()));

            // add setter
            cc.addMethod(generateSetter(cc, entry.getKey(), entry.getValue()));
        }

        cc.addMethod(generateInitMethod(cc, properties));
        return cc.toClass();
    }

    private static CtMethod generateGetter(CtClass declaringClass, String fieldName, Class fieldClass)
            throws CannotCompileException {

        String getterName = "get" + fieldName.substring(0, 1).toUpperCase()
                + fieldName.substring(1);

        StringBuffer sb = new StringBuffer();
        sb.append("public ").append(fieldClass.getName()).append(" ")
                .append(getterName).append("(){").append("return this.")
                .append(fieldName).append(";").append("}");
        return CtMethod.make(sb.toString(), declaringClass);
    }

    private static CtMethod generateSetter(CtClass declaringClass, String fieldName, Class fieldClass)
            throws CannotCompileException {

        String setterName = "set" + fieldName.substring(0, 1).toUpperCase()
                + fieldName.substring(1);

        StringBuffer sb = new StringBuffer();
        sb.append("public void ").append(setterName).append("(")
                .append(fieldClass.getName()).append(" ").append(fieldName)
                .append(")").append("{").append("this.").append(fieldName)
                .append("=").append(fieldName).append(";").append("}");
        return CtMethod.make(sb.toString(), declaringClass);
    }

    private static CtMethod generateInitMethod(CtClass declaringClass, Map<String, Class<?>> properties)
            throws CannotCompileException {

        String setterName = "Constructor";

        List<String> fields = new ArrayList<>();

        //set attributes names
        properties.forEach((e, v) -> {
            fields.add(e);
        });

        String body = "";

        for (int i = 0; i < fields.size(); i++) {
            body += "this.".concat(fields.get(i)).concat("=").concat("(String)arguments.get(").concat(String.valueOf(i)).concat(")").concat(";");
        }

        StringBuffer sb = new StringBuffer();

        sb.append("public void ").append(setterName).append("(").append("java.util.List arguments").append(")")
                .append("{")
                .append(body)
                .append("}");

        System.out.println(sb);

        return CtMethod.make(sb.toString(), declaringClass);
    }

    private static CtClass resolveCtClass(Class clazz) throws NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        return pool.get(clazz.getName());
    }

}

Извините за некрасивый код, но это только минимальная версия.

Может ли кто-нибудь дать какое-нибудь руководство по решению этой проблемы?

1 Ответ

0 голосов
/ 28 июня 2019

На самом деле это проверка, выполненная библиотекой Javassist, так как вы не можете иметь два одинаковых имени класса и пакета в Java:

Class<?> clazz = PojoGenerator.generate( "net.javaforge.blog.javassist.Pojo$Generated", props);
Class<?> clazz2 = PojoGenerator.generate( "net.javaforge.blog.javassist.Pojo$Generated", props);

Вышеперечисленное потерпит неудачу, поскольку класс Pojo уже сгенерированпервое утверждение.Следующий пример будет работать, когда вы называете второй класс Pojo2:

Class<?> clazz = PojoGenerator.generate( "net.javaforge.blog.javassist.Pojo$Generated", props);
Class<?> clazz2 = PojoGenerator.generate( "net.javaforge.blog.javassist.Pojo2$Generated", props);

. Из вашего полного примера я вижу, что он генерируется динамически каким-то внешним вызовом, поэтому для решенияв этом случае вы можете найти способ сохранить результат генерации вашей таблицы (то есть локальную карту) и вызывать метод генерации, только если он отсутствует.Это было бы что-то вроде:

Map<String, Class<?>> cache = new HashMap<String, Class<?>>();    
...
Class<?> clazz = null;
try {
    if (cache.containsKey("your.package.yourTableName$Generated")) {
        clazz = cache.get("your.package.yourTableName$Generated");
    } else {
        clazz = PojoGenerator.generate( "your.package.yourTableName$Generated", props);
        cache.put("your.package.yourTableName$Generated", clazz);
    }
}...
...