org.dozer.DozerBeanMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dozer Show documentation
Show all versions of dozer Show documentation
Dozer is a powerful Java Bean to Java Bean mapper that recursively copies data from one object to another
The newest version!
/**
* Copyright 2005-2013 Dozer Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dozer;
import org.dozer.cache.CacheManager;
import org.dozer.cache.DozerCacheManager;
import org.dozer.cache.DozerCacheType;
import org.dozer.classmap.ClassMappings;
import org.dozer.classmap.Configuration;
import org.dozer.classmap.MappingFileData;
import org.dozer.config.GlobalSettings;
import org.dozer.event.DozerEventManager;
import org.dozer.factory.DestBeanCreator;
import org.dozer.loader.CustomMappingsLoader;
import org.dozer.loader.LoadMappingsResult;
import org.dozer.loader.api.BeanMappingBuilder;
import org.dozer.loader.xml.MappingFileReader;
import org.dozer.loader.xml.MappingStreamReader;
import org.dozer.loader.xml.XMLParserFactory;
import org.dozer.metadata.DozerMappingMetadata;
import org.dozer.metadata.MappingMetadata;
import org.dozer.stats.GlobalStatistics;
import org.dozer.stats.StatisticType;
import org.dozer.stats.StatisticsInterceptor;
import org.dozer.stats.StatisticsManager;
import org.dozer.util.MappingValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Public Dozer Mapper implementation. This should be used/defined as a singleton within your application. This class
* performs several one-time initializations and loads the custom xml mappings, so you will not want to create many
* instances of it for performance reasons. Typically a system will only have one DozerBeanMapper instance per VM. If
* you are using an IOC framework (i.e Spring), define the Mapper as singleton="true". If you are not using an IOC
* framework, a DozerBeanMapperSingletonWrapper convenience class has been provided in the Dozer jar.
*
* It is technically possible to have multiple DozerBeanMapper instances initialized, but it will hinder internal
* performance optimizations such as caching.
*
* @author tierney.matt
* @author garsombke.franz
* @author dmitry.buzdin
* @author suwarnaratana.arm
*/
public class DozerBeanMapper implements Mapper {
private final Logger log = LoggerFactory.getLogger(DozerBeanMapper.class);
private final StatisticsManager statsMgr = GlobalStatistics.getInstance().getStatsMgr();
private final AtomicBoolean initializing = new AtomicBoolean(false);
private final CountDownLatch ready = new CountDownLatch(1);
/*
* Accessible for custom injection
*/
private final List mappingFiles = new ArrayList();
private final List customConverters = new ArrayList();
private final List builderMappings = new ArrayList();
private final List eventListeners = new ArrayList();
private final Map customConvertersWithId = new HashMap();
private CustomFieldMapper customFieldMapper;
/*
* Not accessible for injection
*/
private ClassMappings customMappings;
private Configuration globalConfiguration;
// There are no global caches. Caches are per bean mapper instance
private final CacheManager cacheManager = new DozerCacheManager();
private DozerEventManager eventManager;
public DozerBeanMapper() {
this(Collections.emptyList());
}
public DozerBeanMapper(List mappingFiles) {
this.mappingFiles.addAll(mappingFiles);
init();
}
/**
* {@inheritDoc}
*/
public void map(Object source, Object destination, String mapId) throws MappingException {
getMappingProcessor().map(source, destination, mapId);
}
/**
* {@inheritDoc}
*/
public T map(Object source, Class destinationClass, String mapId) throws MappingException {
return getMappingProcessor().map(source, destinationClass, mapId);
}
/**
* {@inheritDoc}
*/
public T map(Object source, Class destinationClass) throws MappingException {
return getMappingProcessor().map(source, destinationClass);
}
/**
* {@inheritDoc}
*/
public void map(Object source, Object destination) throws MappingException {
getMappingProcessor().map(source, destination);
}
/**
* Returns list of provided mapping file URLs
*
* @return unmodifiable list of mapping files
*/
public List getMappingFiles() {
return Collections.unmodifiableList(mappingFiles);
}
/**
* Sets list of URLs for custom XML mapping files, which are loaded when mapper gets initialized.
* It is possible to load files from file system via file: prefix. If no prefix is given mapping files are
* loaded from classpath and can be packaged along with the application.
*
* @param mappingFileUrls URLs referencing custom mapping files
* @see java.net.URL
*/
public void setMappingFiles(List mappingFileUrls) {
checkIfInitialized();
this.mappingFiles.clear();
this.mappingFiles.addAll(mappingFileUrls);
}
public void setFactories(Map factories) {
checkIfInitialized();
DestBeanCreator.setStoredFactories(factories);
}
public void setCustomConverters(List customConverters) {
checkIfInitialized();
this.customConverters.clear();
this.customConverters.addAll(customConverters);
}
public List getCustomConverters() {
return Collections.unmodifiableList(customConverters);
}
public Map getCustomConvertersWithId() {
return Collections.unmodifiableMap(customConvertersWithId);
}
private void init() {
DozerInitializer.getInstance().init();
log.info("Initializing a new instance of dozer bean mapper.");
// initialize any bean mapper caches. These caches are only visible to the bean mapper instance and
// are not shared across the VM.
GlobalSettings globalSettings = GlobalSettings.getInstance();
cacheManager.addCache(DozerCacheType.CONVERTER_BY_DEST_TYPE.name(), globalSettings.getConverterByDestTypeCacheMaxSize());
cacheManager.addCache(DozerCacheType.SUPER_TYPE_CHECK.name(), globalSettings.getSuperTypesCacheMaxSize());
// stats
statsMgr.increment(StatisticType.MAPPER_INSTANCES_COUNT);
}
public void destroy() {
DozerInitializer.getInstance().destroy();
}
protected Mapper getMappingProcessor() {
initMappings();
Mapper processor = new MappingProcessor(customMappings, globalConfiguration, cacheManager, statsMgr, customConverters,
eventManager, getCustomFieldMapper(), customConvertersWithId);
// If statistics are enabled, then Proxy the processor with a statistics interceptor
if (statsMgr.isStatisticsEnabled()) {
processor = (Mapper) Proxy.newProxyInstance(processor.getClass().getClassLoader(),
processor.getClass().getInterfaces(),
new StatisticsInterceptor(processor, statsMgr));
}
return processor;
}
void loadCustomMappings() {
CustomMappingsLoader customMappingsLoader = new CustomMappingsLoader();
List xmlMappings = loadFromFiles(mappingFiles);
ArrayList allMappings = new ArrayList();
allMappings.addAll(xmlMappings);
allMappings.addAll(builderMappings);
LoadMappingsResult loadMappingsResult = customMappingsLoader.load(allMappings);
this.customMappings = loadMappingsResult.getCustomMappings();
this.globalConfiguration = loadMappingsResult.getGlobalConfiguration();
}
private List loadFromFiles(List mappingFiles) {
MappingFileReader mappingFileReader = new MappingFileReader(XMLParserFactory.getInstance());
List mappingFileDataList = new ArrayList();
if (mappingFiles != null && mappingFiles.size() > 0) {
log.info("Using the following xml files to load custom mappings for the bean mapper instance: {}", mappingFiles);
for (String mappingFileName : mappingFiles) {
log.info("Trying to find xml mapping file: {}", mappingFileName);
URL url = MappingValidator.validateURL(mappingFileName);
log.info("Using URL [" + url + "] to load custom xml mappings");
MappingFileData mappingFileData = mappingFileReader.read(url);
log.info("Successfully loaded custom xml mappings from URL: [{}]", url);
mappingFileDataList.add(mappingFileData);
}
}
return mappingFileDataList;
}
/**
* Add mapping XML from InputStream resources for mapping not stored in
* files (e.g. from database.) The InputStream will be read immediately to
* internally create MappingFileData objects so that the InputStreams may be
* closed after the call to this method.
*
* @param xmlStream Dozer mapping XML InputStream
*/
public void addMapping(InputStream xmlStream) {
checkIfInitialized();
MappingStreamReader fileReader = new MappingStreamReader(XMLParserFactory.getInstance());
MappingFileData mappingFileData = fileReader.read(xmlStream);
builderMappings.add(mappingFileData);
}
/**
* Adds API mapping to given mapper instance.
*
* @param mappingBuilder mappings to be added
*/
public void addMapping(BeanMappingBuilder mappingBuilder) {
checkIfInitialized();
MappingFileData mappingFileData = mappingBuilder.build();
builderMappings.add(mappingFileData);
}
public List extends DozerEventListener> getEventListeners() {
return Collections.unmodifiableList(eventListeners);
}
public void setEventListeners(List extends DozerEventListener> eventListeners) {
checkIfInitialized();
this.eventListeners.clear();
this.eventListeners.addAll(eventListeners);
}
public CustomFieldMapper getCustomFieldMapper() {
return customFieldMapper;
}
public void setCustomFieldMapper(CustomFieldMapper customFieldMapper) {
checkIfInitialized();
this.customFieldMapper = customFieldMapper;
}
/**
* The {@link org.dozer.metadata.MappingMetadata} interface can be used to query information about the current
* mapping definitions. It provides read only access to all important classes and field
* mapping properties. When first called, initializes all mappings if map() has not yet been called.
*
* @return An instance of {@line org.dozer.metadata.MappingMetadata} which serves starting point
* for querying mapping information.
*/
public MappingMetadata getMappingMetadata() {
initMappings();
return new DozerMappingMetadata(customMappings);
}
/**
* Converters passed with this method could be further referenced in mappings via its unique id.
* Converter instances passed that way are considered stateful and will not be initialized for each mapping.
*
* @param customConvertersWithId converter id to converter instance map
*/
public void setCustomConvertersWithId(Map customConvertersWithId) {
checkIfInitialized();
this.customConvertersWithId.clear();
this.customConvertersWithId.putAll(customConvertersWithId);
}
private void checkIfInitialized() {
if (ready.getCount() == 0) {
throw new MappingException("Dozer Bean Mapper is already initialized! Modify settings before calling map()");
}
}
private void initMappings() {
if (initializing.compareAndSet(false, true)) {
try {
loadCustomMappings();
eventManager = new DozerEventManager(eventListeners);
} catch (RuntimeException e) {
// reset initialized state if error happens
initializing.set(false);
throw e;
} finally {
ready.countDown();
}
}
try {
ready.await();
} catch (InterruptedException e) {
log.error("Thread interrupted: ", e);
// Restore the interrupted status:
Thread.currentThread().interrupt();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy