DozerMapper / dozer

Dozer is a Java Bean to Java Bean mapper that recursively copies data from one object to another.
https://dozermapper.github.io/
Apache License 2.0
2.08k stars 481 forks source link

protobuf: incorrect class FQN for nested message types #696

Closed obno closed 6 years ago

obno commented 6 years ago

Whats your runtime?

Whats the problem?

This is related to #275 and #288, which do not take into account nested protobuf message types.

Steps to reproduce:

  1. Create a nested message type. e.g.
syntax = "proto3";

package test.api.v1;

option java_multiple_files = true;
option java_package = "test.api.v1";

message Outer {

  message Inner {
    string prop1 = 1;
  }

  Inner prop1 = 1;
  string prop2 = 2;

}
  1. Create the bean mappings
public class NestedMessage
{
    public static class Inner {

        private String prop1;

        public Inner() {
        }

        public String getProp1() {
            return prop1;
        }

        public void setProp1(String prop1) {
            this.prop1 = prop1;
        }
    }

    public static class Outer {
        private String prop2;
        private Inner prop1;

        public Outer(){

        }

        public String getProp2() {
            return prop2;
        }

        public void setProp2(String prop2) {
            this.prop2 = prop2;
        }

        public Inner getProp1() {
            return prop1;
        }

        public void setProp1(Inner prop1) {
            this.prop1 = prop1;
        }
    }

    @Test
    public void testNestedMessage()
    {
        Mapper mapper = DozerBeanMapperBuilder.create().withMappingBuilders(

                new BeanMappingBuilder() {

                    @Override
                    protected void configure() {

                        mapping(Inner.class, test.api.v1.Outer.Inner.class);
                        mapping(Outer.class, test.api.v1.Outer.class);

                    }

                }

        ).build();

        Outer o = new Outer();
        Inner i = new Inner();
        i.setProp1("1.1");
        o.setProp1(i);
        o.setProp2("1");

        mapper.map(o, test.api.v1.Outer.class); //CNFE!
    }
}

Observed Results:

You get a ClassNotFoundException because Dozer wrongly infers the class FQN for messge Inner as test.api.v1.Inner instead of test.api.v1.Outer.Inner.

com.github.dozermapper.protobuf.util.ProtoUtils.getFullyQualifiedClassName(FileOptions, String) uses the simple class name appended to the package name, ignoring outer message classes names.

com.github.dozermapper.core.MappingException: java.lang.ClassNotFoundException: test.api.v1.Inner
    at com.github.dozermapper.core.util.MappingUtils.throwMappingException(MappingUtils.java:78)
    at com.github.dozermapper.core.util.DefaultClassLoader.loadClass(DefaultClassLoader.java:40)
    at com.github.dozermapper.core.util.MappingUtils.loadClass(MappingUtils.java:222)
    at com.github.dozermapper.protobuf.util.ProtoUtils.getJavaClassIgnoreRepeated(ProtoUtils.java:209)
    at com.github.dozermapper.protobuf.util.ProtoUtils.getJavaClass(ProtoUtils.java:171)
    at com.github.dozermapper.protobuf.propertydescriptor.ProtoFieldPropertyDescriptor.getPropertyType(ProtoFieldPropertyDescriptor.java:100)
    at com.github.dozermapper.core.fieldmap.FieldMap.getDestFieldType(FieldMap.java:120)
    at com.github.dozermapper.core.util.MappingUtils.applyGlobalCopyByReference(MappingUtils.java:211)
    at com.github.dozermapper.core.classmap.generator.GeneratorUtils.addGenericMapping(GeneratorUtils.java:67)
    at com.github.dozermapper.core.classmap.generator.BeanMappingGenerator.apply(BeanMappingGenerator.java:80)
    at com.github.dozermapper.core.classmap.ClassMapBuilder.generateMapping(ClassMapBuilder.java:140)
    at com.github.dozermapper.core.classmap.ClassMapBuilder.addDefaultFieldMappings(ClassMapBuilder.java:129)
    at com.github.dozermapper.core.loader.CustomMappingsLoader.load(CustomMappingsLoader.java:67)
    at com.github.dozermapper.core.DozerBeanMapperBuilder.loadCustomMappings(DozerBeanMapperBuilder.java:741)
    at com.github.dozermapper.core.DozerBeanMapperBuilder.build(DozerBeanMapperBuilder.java:588)
    at dozer.test.NestedMessagesTest.testNestedMessage(NestedMessagesTest.java:33)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: java.lang.ClassNotFoundException: test.api.v1.Inner
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at org.apache.commons.lang3.ClassUtils.getClass(ClassUtils.java:993)
    at org.apache.commons.lang3.ClassUtils.getClass(ClassUtils.java:1027)
    at com.github.dozermapper.core.util.DefaultClassLoader.loadClass(DefaultClassLoader.java:35)
    ... 37 more

Expected Results:

Link to GitHub repo with Unit test

https://github.com/obno/dozer.git

garethahealy commented 6 years ago

@obno ; are you able to raise a PR to fix?

obno commented 6 years ago

@garethahealy , sure i'll give it a go.

Also - same problem for enums nested inside messages:

message Colors {
    enum Color {
        red   = 0;
        blue  = 1;
        green = 2;
    }

    Color color = 1;
}