You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "Sergey Zhemzhitsky (Issue Comment Edited) (JIRA)" <ji...@apache.org> on 2012/03/22 13:48:22 UTC

[jira] [Issue Comment Edited] (CAMEL-5031) Dozer Type Conversion Does Not Work With Context Based Mappings (map-id)

    [ https://issues.apache.org/jira/browse/CAMEL-5031?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13235545#comment-13235545 ] 

Sergey Zhemzhitsky edited comment on CAMEL-5031 at 3/22/12 12:48 PM:
---------------------------------------------------------------------

Hi guys,

May be you can find it quite useful, but we were able to workaround such a limitation by create our own converter
{code:java}
public class DozerTypeConverter implements TypeConverter {

    public static final String MAP_ID_PROP = "DozerMapID";

    private DozerBeanMapper mapper;

    public DozerTypeConverter(DozerBeanMapper mapper) {
        this.mapper = mapper;
    }

    public <T> T convertTo(Class<T> type, Object value) {
        return convertTo(type, value, null);
    }

    public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
        return convertTo(type, value, exchange.getProperty(MAP_ID_PROP, null, String.class));
    }

    public <T> T mandatoryConvertTo(Class<T> type, Object value) throws NoTypeConversionAvailableException {
        return convertTo(type, value, null);
    }

    public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) throws NoTypeConversionAvailableException {
        return convertTo(type, value, exchange.getProperty(MAP_ID_PROP, null, String.class));
    }

    private <T> T convertTo(Class<T> type, Object value, String mapId) {
        if(mapId != null) {
            return mapper.map(value, type, mapId);
        }
        return mapper.map(value, type);
    }

}
{code}

Here is how the camel route looks like

{code:xml}
<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route id="startRoute">
        <from uri="direct://start" />
        <setProperty propertyName="DozerMapID">
            <constant>Type2Map</constant>
        </setProperty>
        <convertBodyTo type="java.util.Map"/>
        <to uri="mock://complete"/>
    </route>
</camelContext>
{code}

and mapping file

{code:xml}
<mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">

    <mapping map-id="Type2Map">
        <class-a>org.foo.bar.dozer.DozerTypeConverterTest$Type</class-a>
        <class-b>java.util.HashMap</class-b>
        <field>
            <a>p1</a>
            <b key="P1">this</b>
        </field>
        <field>
            <a>p2</a>
            <b key="P2">this</b>
        </field>
    </mapping>

</mappings>
{code}

We also had to modify DozerTypeConverterLoader to create our converter instead of the camel's one.

{code:java}
public class DozerTypeConverterLoader implements InitializingBean, CamelContextAware {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private DozerBeanMapper mapper;
    private CamelContext camelContext;

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(mapper, "mapper: null");
        Assert.notNull(mapper, "camelContext: null");

        TypeConverterRegistry registry = getTypeConverterRegistry();
        load(registry);
    }

    public void load(TypeConverterRegistry registry) throws Exception {
        List<ClassMap> all = loadMappings(camelContext, mapper);
        registerClassMaps(registry, mapper, all);
    }

    private void registerClassMaps(TypeConverterRegistry registry, DozerBeanMapper dozer, List<ClassMap> all) {
        DozerTypeConverter converter = new DozerTypeConverter(dozer);
        for (ClassMap map : all) {
            if(logger.isInfoEnabled()) {
                logger.info("Added {} -> {} as type converter to: {}",
                        new Object[]{map.getSrcClassName(), map.getDestClassName(), registry});
            }
            registry.addTypeConverter(map.getSrcClassToMap(), map.getDestClassToMap(), converter);
            registry.addTypeConverter(map.getDestClassToMap(), map.getSrcClassToMap(), converter);
        }
    }

    private List<ClassMap> loadMappings(CamelContext camelContext, DozerBeanMapper mapper) {
        List<ClassMap> answer = new ArrayList<ClassMap>();

        // load the class map using the class resolver so we can load from classpath in OSGi
        MappingFileReader reader = new MappingFileReader(XMLParserFactory.getInstance());
        List<String> mappingFiles = mapper.getMappingFiles();
        if (mappingFiles == null) {
            return Collections.emptyList();
        }

        for (String name : mappingFiles) {
            URL url = camelContext.getClassResolver().loadResourceAsURL(name);
            MappingFileData data = reader.read(url);
            answer.addAll(data.getClassMaps());
        }

        return answer;
    }

    @Override
    public CamelContext getCamelContext() {
        return camelContext;
    }

    @Override
    public void setCamelContext(CamelContext camelContext) {
        this.camelContext = camelContext;
    }

    private TypeConverterRegistry getTypeConverterRegistry() throws Exception {
        TypeConverterRegistry registry = camelContext.getTypeConverterRegistry();
        if(registry == null) {
            TypeConverter converter = camelContext.getTypeConverter();
            if (converter != null) {
                Method method = converter.getClass().getMethod("getRegistry");
                registry = (TypeConverterRegistry) method.invoke(converter);
            }
        }
        return registry;
    }

    public void setMapper(DozerBeanMapper mapper) {
        this.mapper = mapper;
    }

}
{code}

Unfortunately the codebase is for camel 2.4, so there are some workarounds for CAMEL-3143 (Take a look at getTypeConverterRegistry method)
                
      was (Author: szhemzhitsky):
    Hi guys,

May be you can find it quite useful, but we were able to workaround such a limitation by create our own converter
{code:java}
public class DozerTypeConverter implements TypeConverter {

    public static final String MAP_ID_PROP = "DozerMapID";

    private DozerBeanMapper mapper;

    public DozerTypeConverter(DozerBeanMapper mapper) {
        this.mapper = mapper;
    }

    public <T> T convertTo(Class<T> type, Object value) {
        return convertTo(type, value, null);
    }

    public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
        return convertTo(type, value, exchange.getProperty(MAP_ID_PROP, null, String.class));
    }

    public <T> T mandatoryConvertTo(Class<T> type, Object value) throws NoTypeConversionAvailableException {
        return convertTo(type, value, null);
    }

    public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) throws NoTypeConversionAvailableException {
        return convertTo(type, value, exchange.getProperty(MAP_ID_PROP, null, String.class));
    }

    private <T> T convertTo(Class<T> type, Object value, String mapId) {
        if(mapId != null) {
            return mapper.map(value, type, mapId);
        }
        return mapper.map(value, type);
    }

}
{code}

Here is how the camel route looks like

{code:xml}
<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route id="startRoute">
        <from uri="direct://start" />
        <setProperty propertyName="DozerMapID">
            <constant>Type2Map</constant>
        </setProperty>
        <convertBodyTo type="java.util.Map"/>
        <to uri="mock://complete"/>
    </route>
</camelContext>
{code}

and mapping file

{code:xml}
<mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">

    <mapping map-id="Type2Map">
        <class-a>ru.troika.cto.stdint.esb.dozer.DozerTypeConverterTest$Type</class-a>
        <class-b>java.util.HashMap</class-b>
        <field>
            <a>p1</a>
            <b key="P1">this</b>
        </field>
        <field>
            <a>p2</a>
            <b key="P2">this</b>
        </field>
    </mapping>

</mappings>
{code}

We also had to modify DozerTypeConverterLoader to create our converter instead of the camel's one.

{code:java}
public class DozerTypeConverterLoader implements InitializingBean, CamelContextAware {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private DozerBeanMapper mapper;
    private CamelContext camelContext;

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(mapper, "mapper: null");
        Assert.notNull(mapper, "camelContext: null");

        TypeConverterRegistry registry = getTypeConverterRegistry();
        load(registry);
    }

    public void load(TypeConverterRegistry registry) throws Exception {
        List<ClassMap> all = loadMappings(camelContext, mapper);
        registerClassMaps(registry, mapper, all);
    }

    private void registerClassMaps(TypeConverterRegistry registry, DozerBeanMapper dozer, List<ClassMap> all) {
        DozerTypeConverter converter = new DozerTypeConverter(dozer);
        for (ClassMap map : all) {
            if(logger.isInfoEnabled()) {
                logger.info("Added {} -> {} as type converter to: {}",
                        new Object[]{map.getSrcClassName(), map.getDestClassName(), registry});
            }
            registry.addTypeConverter(map.getSrcClassToMap(), map.getDestClassToMap(), converter);
            registry.addTypeConverter(map.getDestClassToMap(), map.getSrcClassToMap(), converter);
        }
    }

    private List<ClassMap> loadMappings(CamelContext camelContext, DozerBeanMapper mapper) {
        List<ClassMap> answer = new ArrayList<ClassMap>();

        // load the class map using the class resolver so we can load from classpath in OSGi
        MappingFileReader reader = new MappingFileReader(XMLParserFactory.getInstance());
        List<String> mappingFiles = mapper.getMappingFiles();
        if (mappingFiles == null) {
            return Collections.emptyList();
        }

        for (String name : mappingFiles) {
            URL url = camelContext.getClassResolver().loadResourceAsURL(name);
            MappingFileData data = reader.read(url);
            answer.addAll(data.getClassMaps());
        }

        return answer;
    }

    @Override
    public CamelContext getCamelContext() {
        return camelContext;
    }

    @Override
    public void setCamelContext(CamelContext camelContext) {
        this.camelContext = camelContext;
    }

    private TypeConverterRegistry getTypeConverterRegistry() throws Exception {
        TypeConverterRegistry registry = camelContext.getTypeConverterRegistry();
        if(registry == null) {
            TypeConverter converter = camelContext.getTypeConverter();
            if (converter != null) {
                Method method = converter.getClass().getMethod("getRegistry");
                registry = (TypeConverterRegistry) method.invoke(converter);
            }
        }
        return registry;
    }

    public void setMapper(DozerBeanMapper mapper) {
        this.mapper = mapper;
    }

}
{code}

Unfortunately the codebase is for camel 2.4, so there are some workarounds for CAMEL-3143 (Take a look at getTypeConverterRegistry method)
                  
> Dozer Type Conversion Does Not Work With Context Based Mappings (map-id)
> ------------------------------------------------------------------------
>
>                 Key: CAMEL-5031
>                 URL: https://issues.apache.org/jira/browse/CAMEL-5031
>             Project: Camel
>          Issue Type: Improvement
>          Components: bean-integration, camel-core
>    Affects Versions: 2.6.0, 2.9.0
>            Reporter: Andre Piwoni
>            Priority: Minor
>
> DozerTypeConverter utilizes DozerBeanMappper's method map(Object source, Class<T> destinationClass) to convert objects. Unfortunately, this does not work for Dozer's context based mapping with map-id because it requires call to DozerBeanMappper.map(Object source, Class<T> destinationClass, String mapId).
> Example below does not work without map-id, which is actually required for this type of mapping (see http://dozer.sourceforge.net/documentation/faq.html#complex-to-map)
> <mapping  map-id="myMapping">
>    <class-a>x.y.z.ComplexType</class-a>
>    <class-b>java.util.Map</class-b>
>    <field copy-by-reference="true">
>       <a>complexTypeField</a>
>       <b key="complexTypeFieldKeyName">this</b>
>   </field>
>   ...
> </mapping>
> DozerTypeConverterLoader has access to ClassMap and its map-id, if any, in order to construct a lookup for map-id for a given conversion pair. This is tricky, however, since there can be multiple mappings with different ids for the same conversion pair.
> Interfaces for Dozer and Camel Conversion may not be fully compatible but it would be great if at least limitations can be stated in documentation.
> Regards,
> Andre Piwoni

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira