dimanche 5 juin 2022

Not able to get class with ClassLoader once SpringBoot app completely initialized

I run Spring Boot project in jar and I try to get class by class name from external library with ClassLoader:

Thread.currentThread().getContextClassLoader().loadClass(className)

When I call this from @PostConstruct method it returns correct result but when I call it after app initialization I am getting java.lang.ClassNotFoundException.

I also tried to get class like this but it didn't work correctly once app started:

Class.forName(className, true, Thread.currentThread().getContextClassLoader())

Can somebody please explain what the issue is?





Is the built-in LambdaMetafactory parameter type checking correct?

For example, when I execute the following:

public static int addOne(Number base) {
  return base.intValue() + 1;
}

public static interface AddOneLambda {
  public int addOne(Integer base);
}

public static void main(String[] a) throws Throwable {
  Method lambdaMethod = AddOneLambda.class.getMethod("addOne", Integer.class);
  Class<?>[] lambdaParameters = Stream.of(lambdaMethod.getParameters()).map(p -> p.getType()).toArray(Class[]::new);

  Method sourceMethod = Main.class.getMethod("addOne", Number.class);
  Class<?>[] sourceParameters = Stream.of(sourceMethod.getParameters()).map(p -> p.getType()).toArray(Class[]::new);

  MethodHandles.Lookup lookup = MethodHandles.lookup();
  CallSite site = LambdaMetafactory.metafactory(lookup, //
      lambdaMethod.getName(), //
      MethodType.methodType(lambdaMethod.getDeclaringClass()), //
      MethodType.methodType(lambdaMethod.getReturnType(), lambdaParameters), //
      lookup.unreflect(sourceMethod), //
      MethodType.methodType(sourceMethod.getReturnType(), sourceParameters));

  AddOneLambda addOneLambda = (AddOneLambda) site.getTarget().invoke();
  System.out.println("1 + 1 = " + addOneLambda.addOne(1));
}

I receive the following exception from metafactory:

LambdaConversionException: Type mismatch for dynamic parameter 0: class java.lang.Number is not a subtype of class java.lang.Integer

I don't understand this. Passing an Integer to the AddOneLambda should always be fine, because the underlying addOne method can accept Integers as part of it's Number signature - so I believe this configuration should be "safe".

On the other hand, when I execute the above with this change:

public static int addOne(Integer base) {
  return base.intValue() + 1;
}

public interface AddOneLambda {
  public int addOne(Number base);
}

The metafactory allows now this without exception, but it doesn't seem right. I can pass any kind of Number to AddOneLambda, even though the underlying method can only handle Integers - so I believe this configuration to be "unsafe". Indeed, if I now call addOneLambda.addOne(1.5) I receive an exception for inability to cast Double to Integer.

Why then is my initial code not allowed, while the change which ultimately allows for invalid types to be passed ok? Is it something to do with the values I'm passing to metafactory, is metafactory incorrectly checking the types, or does this prevent some other kind of situation I haven't considered? If relevant, I'm using JDK 17.0.3.





samedi 4 juin 2022

Type.GetField on array field returns null

I'm trying to get an array field from class using Reflections. On a simple field it works, on Array it doesn't.

This is the class

public abstract class Condition : ScriptableObject
{
    public string Name;
    public virtual bool IsVerified() { return false; }
}

public class ExampleScript : MonoBehaviour
{
    [SerializeField] Condition _condition = null;
    [SerializeField] Condition[] _conditions = new Condition[0];
}

[CustomPropertyDrawer(typeof(Condition))]
public class ConditionPropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);

        Type propertyType = GetPropertyType(property);

        EditorGUI.EndProperty();
    }

    private Type GetPropertyType(SerializedProperty property)
    {
        Type parentType = property.serializedObject.targetObject.GetType();
        Debug.Log($"{parentType} => {property.propertyPath}");
        FieldInfo fi = parentType.GetField(property.propertyPath, BindingFlags.NonPublic | BindingFlags.Instance);
        Debug.Log(fi);
        return fi.FieldType;
    }
}

This is where I'm getting Fields:

Type parentType = property.serializedObject.targetObject.GetType();
Debug.Log($"{parentType} => {property.propertyPath}");
FieldInfo fi = parentType.GetField(property.propertyPath, BindingFlags.NonPublic | BindingFlags.Instance);
Debug.Log(fi);

The Debug prints (Condition var):

ExampleScript => _condition
MyFullNameSpace.Condition _condition

The Debug prints (Condition[] var):

ExampleScript => _conditions.Array.data[0]
Null

Why it doesn't return right FieldInfo?
Thanks in advance





jeudi 2 juin 2022

reset static field to their initial value using reflexion in java

I use in my project a lib with some class with a lot of static field and other stuffs, who are indirectly updated and consulted at runtime in my app. I can't update or ignore or mock this class.

How can I re-execute the method who initialise the static field of a class?

I'm currently writing test in my app, which trigger this class. I want the static field of this class to be at their init state each time I start my app.

The issue is, setting manualy those static field is not possible, it would be too complexe. I need them to be put at their initial state "automaticaly"

I can't change anything to the code of the class, because it's in a lib.

Any idea how I can do that? With reflexion, maybe?





How to get static properties from base type via reflection

I try to get static properties from a base type via reflection. There are many questions regarding this topic on this side, but they all focus on getting non static properties of a base type or static properties from the target type.

public class A
{
    public static string STATIC_BaseProp => "STATIC_BaseProp"; //<-- I want this
    public string BaseProp => "BaseProp";
}

public class B : A
{
    public static string STATIC_Prop => "STATIC_Prop";
    public string Prop => "PROP";
}

static void Main(string[] args)
{
    var type = typeof(B);
    foreach (var propertyInfo in type.GetProperties())
    {
        Console.log(propertyInfo);
    }
}

Output:

System.String STATIC_Prop
System.String Prop
System.String BaseProp

This seems to only adress the static properties of the target type and the non static properties of the target type and the base type. But i want the static property of the base type (STATIC_BaseProp)

Does anyone know how to do this?





mercredi 1 juin 2022

Dynamically calling decorated functions in sequence [duplicate]

I want to expose an api endpoint that allows a user to submit a payload w/ a sequence of functions to be called (pre-defined), along with their parameters.

Also, these functions are wrapped with decorators.

What is the proper way of accessing a function by name in python?

example payload:

{
    "step1": {
        "fn": "function_1",
        "params": {
            "input_file": "example.txt"
        }
    },
    "step2": {
        "fn": "function_2",
        "params": {
            "y": 2
        }
    }
}

In this scenario I should have python run function_2(function_1("example.txt")``, kwargs=**step2["params"])





Why do the following code prints the stack trace and why is a.toString() in the return statement not giving an error or exception?

Following is the complete code -

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;

class A2<T> {
    T ob;
    A2(T ob) {
        this.ob = ob;
    }
    @Annos(str="T example")
    T retob() {
        return ob;
    }
    @Override
    public String toString() {
        Annotation a = null;
        try {
            Class<?> c = this.getClass();
            Method m = c.getMethod("retob");
            a = m.getAnnotation(Annos.class);
        }catch(NoSuchMethodException NSMe) {
            NSMe.printStackTrace();
        }
        return a==null?"EMPTY":a.toString();
    }
    public static void main(String args[]) {
        System.out.println(new A2<Integer>(2).toString());
    }
}

@Retention(RetentionPolicy.RUNTIME)
@interface Annos {
    String str();
}

If I have override the toString() method then how can a.toString() in the return statement as well as in the main method work?

Here is the output -

java.lang.NoSuchMethodException: A2.retob()
        at java.base/java.lang.Class.getMethod(Class.java:2227)
        at A2.toString(A2.java:20)
        at A2.main(A2.java:28)
EMPTY

Why is it not able to get the method retob?