Dozer and loading of dozerbeanmapping.dtd from the classpath

If you use the dozer library from http://dozer.sourceforge.net, you will probably hate the fact that your application will try to connect to the internet and download the DTD from http://dozer.sourceforge.net/dtd/dozerbeanmapping.dtd . That is due to the DOCTYPE declaration in the dozer XML mapping file, e.g.:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mappings PUBLIC "-//DOZER//DTD MAPPINGS//EN"
<mappings>
....
</mappings>

The one possibility to prevent this is to copy the dtd file on the file system and use the file:/// URL but that would mean that you have to maintain this file separately from your war file on every deployment. I would like to deliver the DTD packed into my war file, not care about copying the file on the filesystem of the target node but prevent connection to the internet for several reasons (e.g. if the target server is running within a DMZ that does not allow connections to the internet or if the site http://dozer.sourceforge.net is down for maintenance, the application would not start properly).

I could not find a clean way of doing this so I decide to “patch” dozer by redefining the class org.dozer.loader.xml.DozerResolver in my source path that would usually have a precedence over the dozer original class that is located in jar file located in lib directory. Here is a simple implementation that shows how it works:

  • First a download the DTD file http://dozer.sourceforge.net/dtd/dozerbeanmapping.dtd into ../src/main/resources/dozerbeanmapping.dtd (maven standard directory for resource files)
  • Then I patch the DozerResolver in a way that it tries to load the file dozerbeanmapping.dtd from the root directory and if not available, go to the internet
    package org.dozer.loader.xml;
    import java.io.IOException;
    import java.io.InputStream;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.dozer.util.ResourceLoader;
    import org.xml.sax.EntityResolver;
    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    public class DozerResolver implements EntityResolver {
        private static final Log log = LogFactory.getLog(DozerResolver.class);
        @Override
        public InputSource resolveEntity(String publicId, String systemId)
                throws SAXException, IOException {
            log.debug("Trying to resolve XML entity with public ID [" + publicId
                    + "] and system ID [" + systemId + "]");
            // First try to find it in the classpath
            if (systemId != null && !"".equals(systemId)) {
                String sytemIdStripped = systemId;
                    if(systemId.lastIndexOf("/")>0) {
                        sytemIdStripped= systemId.substring(systemId.lastIndexOf("/") );
                    }
                InputSource inputSource = null;
                try {
                    InputStream inputStream = DozerResolver.class
                            .getResourceAsStream(sytemIdStripped);
                    if (inputStream != null) {
                        inputSource = new InputSource(inputStream);
                        return inputSource;
                    }
                } catch (Exception e) {
                    log.debug("not found in the classpath");
                }
            }
            if ((systemId != null)
                    && (systemId.indexOf("beanmapping.xsd") > systemId
                            .lastIndexOf("/"))) {
                String fileName = systemId.substring(systemId
                        .indexOf("beanmapping.xsd"));
                log.debug("Trying to locate [" + fileName + "] in classpath");
                try {
                    ResourceLoader resourceLoader = new ResourceLoader();
                    InputStream stream = resourceLoader.getResource(fileName)
                            .openStream();
                    InputSource source = new InputSource(stream);
                    source.setPublicId(publicId);
                    source.setSystemId(systemId);
                    log.debug("Found beanmapping XML Schema [" + systemId
                            + "] in classpath");
                    return source;
                } catch (Exception ex) {
                    log.error("Could not resolve beansmapping XML Schema ["
                            + systemId + "]: not found in classpath", ex);
                }
            }
            return null;
        }
    }

That’s it! Now your application wouldn’t try to connect to the internet on every start up.

This trick won’t work if you have the dozer jar in a place that is preferred to your WEB-INF/classes by your application server. E.g. if you are using weblogic and for some reason has the dozer jar in your bootstrap it would be preferred. In this case you have to pack this patched class into a separate jar and put it in front of the dozer jar.

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*

18 − four =