DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • Classloaders in JVM: An Overview
  • Java Virtual Machine Internals, Part 1: Class Loader
  • How to Use a Start/Stop Loader on Every AJAX Call

Trending

  • Designing Databases for Distributed Systems
  • REST vs. Message Brokers: Choosing the Right Communication
  • Best Practices for Writing Clean Java Code
  • Software Verification and Validation With Simple Examples
  1. DZone
  2. Software Design and Architecture
  3. Performance
  4. Dozer POJO Mapper in OSGi

Dozer POJO Mapper in OSGi

John Georgiadis user avatar by
John Georgiadis
·
Jun. 13, 14 · Tutorial
Like (0)
Save
Tweet
Share
9.34K Views

Join the DZone community and get the full member experience.

Join For Free

Dozer

Dozer is a popular bean-to-bean mapping library. This functionality is typically needed when switching between domain models, e.g. to isolate the internal representation of a domain model from the model published to clients through a REST API.

Dozer employs a class loader for loading at least the following resources:

  • Mapping definition XML files. A mapping definition file contains custom mapping rules.
  • Classes based on the fully qualified class name using Class.forName().

Dozer has a pluggable class loading configuration which allows to set a custom class loader for classes and resources. However Dozer was not designed with OSGi in mind. Custom class loaders are set on a Dozer singleton. This combined with the lazy loading of mapping definition files can generate unexpected results.

Demonstrating the problem

In an OSGi environment, each bundle’s classes are loaded by the bundle’s class loader. The set of classes available to a bundle includes the classes packaged in the bundle and the classes available through the Import-Package directive.

In order to use Dozer in a bundle, the following directive will need to be added to the

MANIFEST.MF:Import-Package: org.dozer;version="[5.5,6)"

The mapping definition file dozer.xml can be placed in the bundle, e.g. in the bundle's top-level directory.

In the bundle, the DozerBeanMapper will have to be instantiated and provided with one or more file paths to the mapping definition files:

DozerBeanMapper dozerBeanMapper = new DozerBeanMapper();
dozerBeanMapper.setMappingFiles(
new ArrayList<String>(Arrays.asList(new String[]{"dozer.xml"})));

Then the mapper can be used to map class com.domain.a.A to com.domain.b.B with the following code:

A a = new A();
B b = dozerBeanMapper.map(a, B.class);

Unfortunately this will fail. Upon calling DozerBeanMapper.map(), the method will attempt to initialize the mappings by loading the mapping definition files and will fail with an org.dozer.MappingException: Unable to locate dozer mapping file [dozer.xml] in the classpath.

Setting the thread class loader (TCCL) to the bundle’s class loader and Wrapping the call with the TCCL will fix this particular problem creates a recipe for a non-portable fragile implementation. E.g. if Dozer was to be wrapped by a facade class then the facade would always have to be initialized while wrapped by the TCCL otherwise the embedded DozerBeanMapper would fail.

// Provided that 'this' is a bundle class, getClass().getClassLoader()
// references the bundle class loader

ClassLoader threadCl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
try {
    B b = dozerBeanMapper.map(a, B.class);
} finally {
    Thread.currentThread().setContextClassLoader(threadCl);
}

The above approach does not address the core issue which is the fact that Dozer’s default class loader cannot resolve neither the bundle’s classes / file resources, nor any imported classes.

Even if the mapping file is placed at a known location in the filesystem instead of being embedded in the bundle, Dozer will still fail while it parses the mapping file, because it will attempt to resolve the referenced classes using its own org.dozer.util.DefaultClassLoader.

A workaround

Fortunately Dozer allows to replace the default class loader with a custom one:

org.dozer.util.DozerClassLoader classLoader=...;
BeanContainer.getInstance().setClassLoader(classLoader);

The downside is that the custom class loader is set on a singleton, thus it affects all bundles that import Dozer. However if the following conditions are valid then an acceptable workaround can be implemented:

  • Bundles are started sequentially.
  • Dozer is setup completely during the bundle’s startup phase.

The first condition will prevent the class loader property of the BeanContainer to be overriden until a bundle has started.

The second condition will allow Dozer to load any custom mapping files and all classes referenced in them before the BeanContainer singleton is reset by another bundle.

The only missing pieces are the custom Dozer class loader, and the Dozer initialization in the bundle.

The Dozer class loader that delegates to the bundle’s class loader is shown below:

public class OsgiDozerClassLoader implements DozerClassLoader {
    private BundleContext context;

    @Override
    public Class<?> loadClass(String className) {
        try {
            return context.getBundle().loadClass(className);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

    @Override
    public URL loadResource(String uri) {
        URL url;

        url = context.getBundle().getResource(uri);

        if (url == null) {
            url = DozerClassLoader.class.getClassLoader().getResource(uri);
        }

        return url;
    }

    public void setContext(BundleContext context) {
        this.context = context;
    }
}

Then during bundle startup – e.g. in a blueprint eagerly created bean constructor or init-method – setup the custom classloader, the mapping files and force parsing of the mapping files:

public DozerBeanMapper getMapper(List<String> files) {
    BeanContainer.getInstance().setClassLoader(classLoader);

    DozerBeanMapper mapper = new DozerBeanMapper();
    mapper.setMappingFiles(files);

    // Force loading of the dozer.xml now instead of loading it
    // upon the first mapping call
    mapper.getMappingMetadata();

    return mapper;
}

The above approach is not optimal. In practice it seems to work and its a viable workaround until singletons are removed & class loading becomes more OSGi-friendly in Dozer.

Acknowledgements

This technique is inspired by a similar approach used in the camel-dozer component.

Loader (equipment)

Published at DZone with permission of John Georgiadis. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Classloaders in JVM: An Overview
  • Java Virtual Machine Internals, Part 1: Class Loader
  • How to Use a Start/Stop Loader on Every AJAX Call

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: