All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dozer.DozerBeanMapper Maven / Gradle / Ivy

Go to download

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 getEventListeners() { return Collections.unmodifiableList(eventListeners); } public void setEventListeners(List 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