mercredi 2 septembre 2020

How to get rid of IllegalArgumentException

I have a requirement to convert a POJO object to its corresponding protobuf message. I am writing one utility which will use reflection and generate the protobuf message.

I have below POJO models -

public class Person {
   int id;
   Map<String,String> properties;
   String name;
   Address address;
   Gender gender;
}

public class Address {
   private String line1;
   private String line2;
   private String line3;
   private String City;
   private String state;
}

public enum Gender {
   MALE,FEMALE
}

Corresponding proto messages -

message PersonMsg {
  int32 id = 1;
  map<string, string> properties = 2;
  string name = 3;
  AddressMsg address = 4;
  Gender gender = 5;
}

message AddressMsg {
  string line1 = 1;
  string line2 = 2;
  string line3 = 3;
  string city = 4;
  string state = 5;
  string country = 6;
  string pincode = 7;

}

enum Gender{
  Gender_DEFAULT = 0;
  Gender_MALE = 1;
  Gender_FEMALE = 2;
}

The converter logic is like - for scalar/primitive types dynamically invoke the gette/setters and populate the data in the protobuf builder. If fields are custom type (in this case Address field), then recursively call the method and populate the field -

private static GeneratedMessageV3 getMessage(Object o) throws ClassNotFoundException
    String protoName = o.getClass().getName()+"Proto";
    Class outerClass = Class.forName(protoName);
    String msgName = protoName+"$"+ o.getClass().getSimpleName()+"Msg";
    Class modelClass = o.getClass();
    Class protoMsg = Class.forName(msgName);

    try {
        Method method = protoMsg.getMethod("newBuilder", null);
        com.google.protobuf.GeneratedMessageV3.Builder builder =  (com.google.protobuf.GeneratedMessageV3.Builder)method.invoke(null, null);

        Field[] fields = o.getClass().getDeclaredFields();
        for(Field field : fields){
            if(field.getType().isPrimitive() || field.getType().isAssignableFrom(String.class)){
                Method getMethod = modelClass.getMethod("get"+ CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field.getName()));
                Object value = getMethod.invoke(o, null);
                Method setMethod = findSetterMethod(builder.getClass(), field);
                setMethod.invoke(builder, value);
            } else if(field.getType().isEnum()){
                Method getMethod = modelClass.getMethod("get"+ CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field.getName()));
                Enum modelEnumValue = (Enum) getMethod.invoke(o, null);
                String protoEnumClassName = field.getType().getName()+"Proto$"+field.getType().getSimpleName();
                Class protoEnumClass = Class.forName(protoEnumClassName);
                Method enumMethod = protoEnumClass.getDeclaredMethod("values", null);
                Enum[] protoValues = (Enum[]) enumMethod.invoke(null, null);
                Enum protoValue = null;
                for(Enum value : protoValues){
                    if(value.name().replace(field.getType().getSimpleName()+"_", "").equals(modelEnumValue.name())){
                        protoValue = value;
                    }
                }
                if(protoValue!=null) {
                    Method setMethod = findSetterMethod(builder.getClass(), field);
                    setMethod.invoke(builder, protoValue);
                }
            } else if(Collection.class.isAssignableFrom(field.getType()) || Map.class.isAssignableFrom(field.getType())){
                if(Map.class.isAssignableFrom(field.getType())){
                    populateMap(o, field, builder);
                }
            } else{
                System.out.println("Field is custom type :"+ field.getName());
                Method getMethod = modelClass.getMethod("get"+ CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field.getName()));
                System.out.println("Found Getter Method :"+ getMethod);
                Object modelValue = getMethod.invoke(o, null);
                System.out.println("Model Value :"+ modelValue);
                Method setMethod = findSetterMethod(builder.getClass(), field);
                setMethod.invoke(builder, getMessage(modelValue));
            }
        }
        GeneratedMessageV3 msg = (GeneratedMessageV3) builder.build();
        return msg;
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}```

This code works fine and convert the projo objects to protobuf message. But sometimes it fails at runtime with - "java.lang.IllegalArgumentException: argument type mismatch"

This is failing at line - "setMethod.invoke(builder, getMessage(modelValue));"

Any idea why this is failing if code is not changed at all ? How can i get rid/workaround this exception ?





Aucun commentaire:

Enregistrer un commentaire