Question

since one day I'm stuck at this Problem. But first I would like to describe, why I'm going the way which is shown:

We`re building a RESTful API in Java using EE7 and Glassfish4. Authentication and authorisation has to be build by ourselves (student project). So the idea was to add our own annotation for @AccesRight and @Roles. After interpreting the metadata on each set and get method of our models (if declared), the @XmlTransient annotation should be set on runtime, when the user has not the right to see this. In short: granting different access at the model attributes.

I tried to modify the method annotations from _model-class-methods (see method signature) but when I run ".toClass()" it fails, because the WebAppClassLoader already has a class loaded (duplicate entry). So I decided to create a copy with another name of the given model (_model.getClass().getName() + transactionToken). The big problem: I can not cast this copy anymore to the original model (I get ClassCastException). The class and the copyclass is stored in the same class loader.

So i considered to invoke a method like e.g. "loadModelByEntity(UserModel _model)" which is stored in all models. The problem is: after running .toClass() of my copy class the method signature now looks like the following: loadModelByEntity(UserModel020a8e6bb07c65da3e9095368db34e843c0b0d1e _model)

Javassist is changing ALL datatypes in the class.

Is there any way to prevent this or to fill my copy model with data? Is there any way to cast my copy models?

Many thanks! Phil

//my annotation interface
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.TYPE, ElementType.METHOD} )
public @interface AccessRight
{
    String  name()    default "";
    Role[]  roles()   default {};
    boolean self()    default true;
    boolean friends() default true;
}




//my method where i analyse the annotation (and perhaps set a new one)
public Object filter(Object _model, String _transactionToken) throws Exception
{

    String className  = _model.getClass().getName() + transactionToken;
    ClassPool pool    = ClassPool.getDefault();    
    CtClass copyClass = pool.getOrNull(className);

    if(copyClass != null)
    {
        Class filterModel     = copyClass.getClass().getClassLoader().loadClass(className);
        Object filterInstance = filterModel.newInstance();

        filterInstance.getClass().getDeclaredMethod("loadByEntity", _model.getClass()).invoke(filterInstance, _model);

        return filterInstance;
    }

    pool.insertClassPath(new ClassClassPath(_model.getClass()));         

    pool.makeClass(className);
    copyClass = pool.getAndRename(_model.getClass().getName(), className);

    ClassFile copyClassFile = copyClass.getClassFile();
    ConstPool constPool = copyClassFile.getConstPool();

    AnnotationsAttribute attribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
    Annotation          annotation = attribute.getAnnotation("javax.xml.bind.annotation.XmlTransient");

    if(annotation == null)
    {
        attribute.addAnnotation(new Annotation("javax.xml.bind.annotation.XmlTransient", constPool));
    }

    for(CtMethod method : copyClass.getDeclaredMethods())
    {
        if(method.hasAnnotation(AccessRight.class))
        {
            AccessRight arAnnotation = (AccessRight)method.getAnnotation(AccessRight.class);

            if(!checkAccess(arAnnotation.name(), arAnnotation.roles(), arAnnotation.friends(), arAnnotation.self()))
            {
                method.getMethodInfo().addAttribute(attribute);
            }
        }
    }

    return copyClass.toClass().newInstance();
}


//my consideration to fill the copy model (but it doesn`t work, like i described)
public void loadByEntity(UserModel _model)
{     
    this.m_id               = _model.getId();
    this.m_firstname        = _model.getFirstname();
    this.m_lastname         = _model.getLastname();
    this.m_username         = _model.getUsername();
    this.m_birthday         = _model.getBirthday();
    this.m_email            = _model.getEmail();
    this.m_password         = _model.getPassword();
    this.m_roleId           = _model.getRoleId();
    this.m_timestampCreated = _model.getTimestampCreated();
    this.m_accessRightList  = _model.getAccesRightList();
}
Was it helpful?

Solution

I solved the problem by deleting the method "loadByEntity" (this is Settings.ENTITY_LOAD_METHODNAME) on runtime in the copy class. Then I readded the method to the copy class with a custom Signature and the javassist codeAttribute from the original class. Also I added the original class as the superclass for casting issues. So my signature looks nice and I`m able to cast to the original model. The methods are ALL overwritten now, because the signature is the same.

    String className  = _model.getClass().getName() + _transactionToken + Helper.getUnixTimestamp() / Math.random();

    ClassPool pool    = ClassPool.getDefault();    
    pool.insertClassPath(new ClassClassPath(_model.getClass())); 

    CtClass copyClass       = pool.getAndRename(_model.getClass().getName(),className);
    CtClass originalClass   = pool.get(_model.getClass().getName());
    ClassFile copyClassFile = copyClass.getClassFile();
    ConstPool constPool     = copyClassFile.getConstPool();

    copyClass.setSuperclass(pool.get(_model.getClass().getName()));
    copyClass.removeMethod(copyClass.getDeclaredMethod(Settings.ENTITY_LOAD_METHODNAME));

    //creates a new method without codeattribute BUT(!) it is abstract
    CtMethod newLoadMethod = new CtMethod(CtClass.voidType, Settings.ENTITY_LOAD_METHODNAME, new CtClass[] {originalClass}, copyClass);
    CtMethod oldLoadMethod = originalClass.getDeclaredMethod(Settings.ENTITY_LOAD_METHODNAME);

    //set modifier to NOT abstract
    newLoadMethod.setModifiers(newLoadMethod.getModifiers() & ~Modifier.ABSTRACT);
    //set the old code attribute
    newLoadMethod.getMethodInfo().setCodeAttribute(oldLoadMethod.getMethodInfo().getCodeAttribute());
    copyClass.addMethod(newLoadMethod);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top