diff --git a/src/main/java/io/beanmapper/config/OverrideField.java b/src/main/java/io/beanmapper/config/OverrideField.java index ab75a13..fde4bca 100644 --- a/src/main/java/io/beanmapper/config/OverrideField.java +++ b/src/main/java/io/beanmapper/config/OverrideField.java @@ -2,6 +2,8 @@ import java.util.function.Supplier; +import io.beanmapper.utils.BeanMapperPerformanceLogger; + public class OverrideField { private final Supplier supplier; @@ -31,9 +33,10 @@ public T get() { if (this.block) { return null; } - return this.value == null ? - this.supplier.get() : - this.value; + if (this.value == null) { + this.value = BeanMapperPerformanceLogger.runTimed("%s#%s -> %s#%s".formatted(this.getClass().getSimpleName(), "get(void)", this.getClass().getSimpleName(), "get(void)"), this.supplier); + } + return value; } -} \ No newline at end of file +} diff --git a/src/main/java/io/beanmapper/config/StrictMappingProperties.java b/src/main/java/io/beanmapper/config/StrictMappingProperties.java index b7f4f01..ea87a74 100644 --- a/src/main/java/io/beanmapper/config/StrictMappingProperties.java +++ b/src/main/java/io/beanmapper/config/StrictMappingProperties.java @@ -2,6 +2,7 @@ import io.beanmapper.core.unproxy.BeanUnproxy; import io.beanmapper.core.unproxy.SkippingBeanUnproxy; +import io.beanmapper.utils.BeanMapperPerformanceLogger; public class StrictMappingProperties { @@ -79,18 +80,20 @@ public void setApplyStrictMappingConvention(boolean applyStrictMappingConvention } public BeanPair createBeanPair(Class sourceClass, Class targetClass) { - sourceClass = beanUnproxy.unproxy(sourceClass); - targetClass = beanUnproxy.unproxy(targetClass); - BeanPair beanPair = new BeanPair(sourceClass, targetClass); + BeanPair beanPair = BeanMapperPerformanceLogger.runTimed("%s#%s".formatted(this.getClass().getSimpleName(), "createBeanPair(Class, Class) -> BeanUnproxy#unproxy(Class)"), () -> { + Class unproxiedSource = beanUnproxy.unproxy(sourceClass); + Class unproxiedTarget = beanUnproxy.unproxy(targetClass); + return new BeanPair(unproxiedSource, unproxiedTarget); + }); if (!isApplyStrictMappingConvention()) { return beanPair; } if (strictSourceSuffix != null && - sourceClass.getCanonicalName().endsWith(strictSourceSuffix)) { + beanPair.getSourceClass().getCanonicalName().endsWith(strictSourceSuffix)) { beanPair = beanPair.withStrictSource(); } if (strictTargetSuffix != null && - targetClass.getCanonicalName().endsWith(strictTargetSuffix)) { + beanPair.getTargetClass().getCanonicalName().endsWith(strictTargetSuffix)) { beanPair = beanPair.withStrictTarget(); } return beanPair; diff --git a/src/main/java/io/beanmapper/core/BeanMatch.java b/src/main/java/io/beanmapper/core/BeanMatch.java index c111dfe..5437ed1 100644 --- a/src/main/java/io/beanmapper/core/BeanMatch.java +++ b/src/main/java/io/beanmapper/core/BeanMatch.java @@ -8,8 +8,13 @@ import io.beanmapper.config.BeanPair; import io.beanmapper.exceptions.BeanNoSuchPropertyException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class BeanMatch { + private static final Logger log = LoggerFactory.getLogger(BeanMatch.class); + private final BeanPair beanPair; private final Map sourceNodes; @@ -106,6 +111,7 @@ private void checkForMandatoryUnmatchedNodes(String side, Class containingCla for (Map.Entry entry : nodes.entrySet()) { BeanProperty currentField = entry.getValue(); if (currentField.isUnmatched()) { + log.error("{} {} has no match for property {}", side, containingClass.getCanonicalName() != null ? containingClass.getCanonicalName() : containingClass.getDeclaringClass().getCanonicalName(), entry.getKey()); throw new BeanNoSuchPropertyException(side + " " + containingClass.getCanonicalName() + " has no match for property " + entry.getKey()); } } diff --git a/src/main/java/io/beanmapper/core/BeanMatchStore.java b/src/main/java/io/beanmapper/core/BeanMatchStore.java index 0bd3e1d..f13fc85 100644 --- a/src/main/java/io/beanmapper/core/BeanMatchStore.java +++ b/src/main/java/io/beanmapper/core/BeanMatchStore.java @@ -34,15 +34,11 @@ import io.beanmapper.exceptions.BeanMissingPathException; import io.beanmapper.exceptions.BeanNoSuchPropertyException; import io.beanmapper.exceptions.FieldShadowingException; +import io.beanmapper.utils.BeanMapperTraceLogger; import io.beanmapper.utils.Trinary; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class BeanMatchStore { - private final Logger logger = LoggerFactory.getLogger(getClass()); - private final CollectionHandlerStore collectionHandlerStore; private final BeanUnproxy beanUnproxy; @@ -287,11 +283,11 @@ private BeanPropertyWrapper dealWithBeanProperty(BeanPropertyMatchupDirection ma new BeanPropertyCreator(matchupDirection.getInverse(), otherType, wrapper.getName()) .determineNodesForPath()); } catch (BeanNoSuchPropertyException err) { - if (logger.isDebugEnabled()) { - logger.debug(""" + + BeanMapperTraceLogger.log(""" BeanNoSuchPropertyException thrown by BeanMatchStore#dealWithBeanProperty(BeanPropertyMatchupDirection, Map, Class, PropertyAccessor), for {}. {}""", wrapper.getName(), err.getMessage()); - } + } } return wrapper; diff --git a/src/main/java/io/beanmapper/core/BeanPropertyCreator.java b/src/main/java/io/beanmapper/core/BeanPropertyCreator.java index 560120d..324c834 100644 --- a/src/main/java/io/beanmapper/core/BeanPropertyCreator.java +++ b/src/main/java/io/beanmapper/core/BeanPropertyCreator.java @@ -6,8 +6,13 @@ import io.beanmapper.core.inspector.PropertyAccessors; import io.beanmapper.exceptions.BeanNoSuchPropertyException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class BeanPropertyCreator { + private static final Logger log = LoggerFactory.getLogger(BeanPropertyCreator.class); + private final BeanPropertyMatchupDirection matchupDirection; private final Class baseClass; @@ -68,6 +73,7 @@ private void traversePath(Stack beanProperties) { for (String node : route.getRoute()) { final PropertyAccessor property = PropertyAccessors.findProperty(currentBaseClass, node); if (property == null) { + log.error("Property '{}' does not exist in: {}", node, currentBaseClass.getSimpleName()); throw new BeanNoSuchPropertyException("Property '" + node + "' does not exist in: " + currentBaseClass.getSimpleName()); } beanProperties.push(new BeanProperty( diff --git a/src/main/java/io/beanmapper/core/BeanPropertyMatch.java b/src/main/java/io/beanmapper/core/BeanPropertyMatch.java index 031883c..ea5ac42 100644 --- a/src/main/java/io/beanmapper/core/BeanPropertyMatch.java +++ b/src/main/java/io/beanmapper/core/BeanPropertyMatch.java @@ -10,13 +10,14 @@ import io.beanmapper.exceptions.BeanMappingException; import io.beanmapper.exceptions.BeanNoLogicSecuredCheckSetException; import io.beanmapper.exceptions.BeanNoRoleSecuredCheckSetException; +import io.beanmapper.utils.BeanMapperTraceLogger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BeanPropertyMatch { - protected final Logger logger = LoggerFactory.getLogger(getClass()); + private static final Logger log = LoggerFactory.getLogger(BeanPropertyMatch.class); private final BeanMatch beanMatch; private final Object source; @@ -81,7 +82,7 @@ private boolean checkForLogicSecured( if (enforcedSecuredProperties) { throw new BeanNoLogicSecuredCheckSetException(message); } - logger.warn(message); + BeanMapperTraceLogger.log(message); return true; } return logicSecuredCheck.isAllowed(source, target); @@ -96,7 +97,7 @@ private void checkIfSecuredFieldHandlerNotSet(BeanProperty beanProperty, boolean if (enforcedSecuredProperties) { throw new BeanNoRoleSecuredCheckSetException(message); } - logger.warn(message); + log.warn(message); } } diff --git a/src/main/java/io/beanmapper/core/BeanStrictMappingRequirementsException.java b/src/main/java/io/beanmapper/core/BeanStrictMappingRequirementsException.java index dd86a92..77319a2 100644 --- a/src/main/java/io/beanmapper/core/BeanStrictMappingRequirementsException.java +++ b/src/main/java/io/beanmapper/core/BeanStrictMappingRequirementsException.java @@ -3,13 +3,14 @@ import java.util.Collections; import java.util.List; +import io.beanmapper.utils.BeanMapperTraceLogger; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BeanStrictMappingRequirementsException extends RuntimeException { - protected final transient Logger logger = LoggerFactory.getLogger(getClass()); - + private static final Logger log = LoggerFactory.getLogger(BeanStrictMappingRequirementsException.class); private final List validationMessages; public BeanStrictMappingRequirementsException(BeanMatchValidationMessage validationMessage) { @@ -26,7 +27,7 @@ private void logErrors(List validationMessages) { if (validationMessage.isLogged()) { continue; } - logger.error(""" + log.error(""" Missing matching properties for source [{}] {} > target [{}] {} for fields: """, validationMessage.getSourceClass().getCanonicalName(), @@ -35,7 +36,7 @@ private void logErrors(List validationMessages) { (validationMessage.isTargetStrict() ? "*" : "")); for (BeanProperty field : validationMessage.getFields()) { - logger.error(""" + log.error(""" > {}.{} """, validationMessage.getStrictClass().getSimpleName(), diff --git a/src/main/java/io/beanmapper/core/collections/AbstractCollectionHandler.java b/src/main/java/io/beanmapper/core/collections/AbstractCollectionHandler.java index 22d4094..e0bf205 100644 --- a/src/main/java/io/beanmapper/core/collections/AbstractCollectionHandler.java +++ b/src/main/java/io/beanmapper/core/collections/AbstractCollectionHandler.java @@ -5,6 +5,7 @@ import io.beanmapper.config.CollectionFlusher; import io.beanmapper.core.constructor.DefaultBeanInitializer; import io.beanmapper.exceptions.BeanCollectionUnassignableTargetCollectionTypeException; +import io.beanmapper.utils.BeanMapperPerformanceLogger; import io.beanmapper.utils.Classes; public abstract class AbstractCollectionHandler implements CollectionHandler { @@ -32,14 +33,14 @@ public Object mapItem( BeanMapper beanMapper, Class collectionElementClass, Object source) { - - return beanMapper - .wrap() + return BeanMapperPerformanceLogger.runTimed("%s#%s" + .formatted(this.getClass().getSimpleName(), "mapItem(BeanMapper, Class, Object) -> BeanMapper#map(Object)"), + () -> beanMapper.wrap() .setTargetClass(collectionElementClass) .setCollectionClass(null) .setConverterChoosable(true) .build() - .map(source); + .map(source)); } @Override @@ -90,4 +91,4 @@ public int getGenericParameterIndex() { return 0; } -} \ No newline at end of file +} diff --git a/src/main/java/io/beanmapper/core/constructor/DefaultBeanInitializer.java b/src/main/java/io/beanmapper/core/constructor/DefaultBeanInitializer.java index b03f8ba..db608e0 100644 --- a/src/main/java/io/beanmapper/core/constructor/DefaultBeanInitializer.java +++ b/src/main/java/io/beanmapper/core/constructor/DefaultBeanInitializer.java @@ -12,6 +12,7 @@ import io.beanmapper.BeanMapper; import io.beanmapper.config.BeanMapperBuilder; import io.beanmapper.strategy.ConstructorArguments; +import io.beanmapper.utils.BeanMapperTraceLogger; import io.beanmapper.utils.DefaultValues; import org.slf4j.Logger; @@ -19,13 +20,14 @@ public class DefaultBeanInitializer implements BeanInitializer { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private static final Logger log = LoggerFactory.getLogger(DefaultBeanInitializer.class); /** * {@inheritDoc} */ @Override public T instantiate(Class beanClass, ConstructorArguments arguments) { + BeanMapperTraceLogger.log("Creating a new instance of type %s, using reflection.".formatted(beanClass)); try { if (arguments == null) { return beanClass.getConstructor().newInstance(); @@ -34,7 +36,7 @@ public T instantiate(Class beanClass, ConstructorArguments arguments) { var constructorParameterTypes = Arrays.stream(constructor.getParameters()).map(Parameter::getParameterizedType).toArray(Type[]::new); return beanClass.getConstructor(arguments.getTypes()).newInstance(mapParameterizedArguments(constructorParameterTypes, arguments.getValues())); } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { - logger.error("Could not instantiate bean of class %s. Returning the default value associated with the given type. %s".formatted(beanClass.getName(), + log.error("Could not instantiate bean of class %s. Returning the default value associated with the given type. %s".formatted(beanClass.getName(), e.getMessage())); return DefaultValues.defaultValueFor(beanClass); } diff --git a/src/main/java/io/beanmapper/core/converter/collections/CollectionConverter.java b/src/main/java/io/beanmapper/core/converter/collections/CollectionConverter.java index 41e174a..a8609d5 100644 --- a/src/main/java/io/beanmapper/core/converter/collections/CollectionConverter.java +++ b/src/main/java/io/beanmapper/core/converter/collections/CollectionConverter.java @@ -4,6 +4,7 @@ import io.beanmapper.core.BeanPropertyMatch; import io.beanmapper.core.collections.CollectionHandler; import io.beanmapper.core.converter.BeanConverter; +import io.beanmapper.utils.BeanMapperPerformanceLogger; public class CollectionConverter implements BeanConverter { @@ -24,7 +25,9 @@ public U convert( return targetClass.cast(source); } - return beanMapper.wrap() + return BeanMapperPerformanceLogger.runTimed("%s#%s" + .formatted(this.getClass().getSimpleName(), "convert(BeanMapper, Object, Class, BeanPropertyMatch) -> BeanMapper#map(Object)"), + () -> beanMapper.wrap() .setCollectionClass(collectionHandler.getType()) .setCollectionUsage(beanPropertyMatch.getCollectionInstructions().getBeanCollectionUsage()) .setPreferredCollectionClass(beanPropertyMatch.getCollectionInstructions().getPreferredCollectionClass().getAnnotationClass()) @@ -33,7 +36,7 @@ public U convert( .setTarget(beanPropertyMatch.getTargetObject()) .setUseNullValue() .build() - .map(source); + .map(source)); } @Override diff --git a/src/main/java/io/beanmapper/core/converter/impl/ObjectToOptionalConverter.java b/src/main/java/io/beanmapper/core/converter/impl/ObjectToOptionalConverter.java index 77f9eaa..cf72c21 100644 --- a/src/main/java/io/beanmapper/core/converter/impl/ObjectToOptionalConverter.java +++ b/src/main/java/io/beanmapper/core/converter/impl/ObjectToOptionalConverter.java @@ -10,6 +10,9 @@ import io.beanmapper.core.converter.BeanConverter; import io.beanmapper.exceptions.BeanNoSuchPropertyException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * This converter facilitates the conversion of an object to an Optional wrapping another object. This converter does * not support the conversion of complex datastructures, such as Collections, to Optionals. If that functionality is @@ -18,6 +21,8 @@ */ public class ObjectToOptionalConverter implements BeanConverter { + private static final Logger log = LoggerFactory.getLogger(ObjectToOptionalConverter.class); + @Override public T convert(BeanMapper beanMapper, S source, Class targetClass, BeanPropertyMatch beanPropertyMatch) { if (source == null) { @@ -28,6 +33,7 @@ public T convert(BeanMapper beanMapper, S source, Class targetClass, B try { targetField = beanPropertyMatch.getTarget().getClass().getDeclaredField(beanPropertyMatch.getTargetFieldName()); } catch (NoSuchFieldException e) { + log.error(e.getMessage()); throw new BeanNoSuchPropertyException(e.getMessage()); } diff --git a/src/main/java/io/beanmapper/core/converter/impl/OptionalToObjectConverter.java b/src/main/java/io/beanmapper/core/converter/impl/OptionalToObjectConverter.java index 5fbc38e..c6e616c 100644 --- a/src/main/java/io/beanmapper/core/converter/impl/OptionalToObjectConverter.java +++ b/src/main/java/io/beanmapper/core/converter/impl/OptionalToObjectConverter.java @@ -12,11 +12,9 @@ import io.beanmapper.BeanMapper; import io.beanmapper.core.BeanPropertyMatch; import io.beanmapper.core.converter.BeanConverter; +import io.beanmapper.utils.BeanMapperTraceLogger; import io.beanmapper.utils.Classes; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * This converter facilitates the conversion of an arbitrary amount of Optional wrappers, however, support for complex * datastructures, such as Maps, Sets, List, etc. is limited to a single layer. As such, if the user requires support @@ -25,8 +23,6 @@ */ public class OptionalToObjectConverter implements BeanConverter { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - /** * {@inheritDoc} */ @@ -35,13 +31,11 @@ public T convert(BeanMapper beanMapper, S source, Class targetClass, B Object obj = ((Optional) source).orElse(null); if (targetClass.equals(Optional.class)) { - if (logger.isDebugEnabled()) { - // Not always possible to get the actual source class, so just report the name of the field. Debug log will show the call stack. - logger.debug("Converting Optional to Optional. Perhaps the target does not need to be an Optional?\nSource-field: {}\nTarget: {}.{}", - beanPropertyMatch.getSourceFieldName(), - beanPropertyMatch.getTarget().getClass(), - beanPropertyMatch.getTargetFieldName()); - } + // Not always possible to get the actual source class, so just report the name of the field. Debug log will show the call stack. + BeanMapperTraceLogger.log("Converting Optional to Optional. Perhaps the target does not need to be an Optional?\nSource-field: {}\nTarget: {}.{}", + beanPropertyMatch.getSourceFieldName(), + beanPropertyMatch.getTarget().getClass(), + beanPropertyMatch.getTargetFieldName()); return targetClass.cast(convertToOptional(beanMapper, (Optional) source, beanPropertyMatch)); } else if (obj == null) { return null; diff --git a/src/main/java/io/beanmapper/core/unproxy/SkippingBeanUnproxy.java b/src/main/java/io/beanmapper/core/unproxy/SkippingBeanUnproxy.java index 643f40f..bddfa09 100644 --- a/src/main/java/io/beanmapper/core/unproxy/SkippingBeanUnproxy.java +++ b/src/main/java/io/beanmapper/core/unproxy/SkippingBeanUnproxy.java @@ -6,6 +6,8 @@ import java.util.HashSet; import java.util.Set; +import io.beanmapper.utils.BeanMapperTraceLogger; + /** * Unproxy that allows you to configure classes to skip. * @@ -31,6 +33,7 @@ public SkippingBeanUnproxy(BeanUnproxy delegate) { */ @Override public Class unproxy(Class beanClass) { + BeanMapperTraceLogger.log("Unproxying class %s.".formatted(beanClass != null ? beanClass.getCanonicalName() : "null")); if (isSkippedProxyClass(beanClass)) { return beanClass; } diff --git a/src/main/java/io/beanmapper/exceptions/BeanNoLogicSecuredCheckSetException.java b/src/main/java/io/beanmapper/exceptions/BeanNoLogicSecuredCheckSetException.java index 065f871..e040703 100644 --- a/src/main/java/io/beanmapper/exceptions/BeanNoLogicSecuredCheckSetException.java +++ b/src/main/java/io/beanmapper/exceptions/BeanNoLogicSecuredCheckSetException.java @@ -1,14 +1,8 @@ package io.beanmapper.exceptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class BeanNoLogicSecuredCheckSetException extends IllegalArgumentException { - protected final transient Logger logger = LoggerFactory.getLogger(getClass()); - public BeanNoLogicSecuredCheckSetException(String message) { super(message); - logger.error(message); } } diff --git a/src/main/java/io/beanmapper/exceptions/BeanNoRoleSecuredCheckSetException.java b/src/main/java/io/beanmapper/exceptions/BeanNoRoleSecuredCheckSetException.java index ffed196..e8041c3 100644 --- a/src/main/java/io/beanmapper/exceptions/BeanNoRoleSecuredCheckSetException.java +++ b/src/main/java/io/beanmapper/exceptions/BeanNoRoleSecuredCheckSetException.java @@ -1,14 +1,8 @@ package io.beanmapper.exceptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class BeanNoRoleSecuredCheckSetException extends IllegalArgumentException { - protected final transient Logger logger = LoggerFactory.getLogger(getClass()); - public BeanNoRoleSecuredCheckSetException(String message) { super(message); - logger.error(message); } } diff --git a/src/main/java/io/beanmapper/exceptions/BeanNoSuchPropertyException.java b/src/main/java/io/beanmapper/exceptions/BeanNoSuchPropertyException.java index c09014b..0caee4d 100644 --- a/src/main/java/io/beanmapper/exceptions/BeanNoSuchPropertyException.java +++ b/src/main/java/io/beanmapper/exceptions/BeanNoSuchPropertyException.java @@ -3,19 +3,12 @@ */ package io.beanmapper.exceptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Exception thrown when a property could not be found. */ public class BeanNoSuchPropertyException extends IllegalArgumentException { - protected final transient Logger logger = LoggerFactory.getLogger(getClass()); - public BeanNoSuchPropertyException(String message) { super(message); - logger.error(message); } - } diff --git a/src/main/java/io/beanmapper/strategy/AbstractMapStrategy.java b/src/main/java/io/beanmapper/strategy/AbstractMapStrategy.java index ba099c1..8b672fe 100644 --- a/src/main/java/io/beanmapper/strategy/AbstractMapStrategy.java +++ b/src/main/java/io/beanmapper/strategy/AbstractMapStrategy.java @@ -12,19 +12,15 @@ import io.beanmapper.core.converter.BeanConverter; import io.beanmapper.exceptions.BeanConversionException; import io.beanmapper.exceptions.BeanPropertyNoMatchException; +import io.beanmapper.utils.BeanMapperTraceLogger; import io.beanmapper.utils.Records; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public abstract class AbstractMapStrategy implements MapStrategy { private static final String INDENT = " "; private static final String ARROW = " -> "; - protected final Logger logger = LoggerFactory.getLogger(getClass()); - private final BeanMapper beanMapper; private final Configuration configuration; @@ -111,7 +107,7 @@ private void dealWithMappableNestedClass(BeanPropertyMatch beanPropertyMatch) { Object encapsulatedSource = beanPropertyMatch.getSourceObject(); Object target; if (encapsulatedSource != null) { - logger.debug(" {"); + BeanMapperTraceLogger.log(" {"); BeanMapper localBeanMapper = getBeanMapper() .wrap() .setParent(beanPropertyMatch.getTarget()) @@ -122,7 +118,7 @@ private void dealWithMappableNestedClass(BeanPropertyMatch beanPropertyMatch) { target = localBeanMapper.map(encapsulatedSource, beanPropertyMatch.getTargetObject()); } beanPropertyMatch.writeObject(target); - logger.debug(" }"); + BeanMapperTraceLogger.log(" }"); } } @@ -139,7 +135,7 @@ public Object convert(Object value, Class targetClass, BeanPropertyMatch bean BeanConverter converter = getConverterOptional(valueClass, targetClass); if (converter != null) { - logger.debug("{}{}{}", INDENT, converter.getClass().getSimpleName(), ARROW); + BeanMapperTraceLogger.log("{}{}{}", INDENT, converter.getClass().getSimpleName(), ARROW); BeanMapper wrappedBeanMapper = beanMapper .wrap() .setParent(beanPropertyMatch.getTarget()) @@ -211,9 +207,9 @@ private void processProperty(BeanPropertyMatch beanPropertyMatch) { return; } if (beanPropertyMatch.isMappable()) { - logger.debug("{}{}", beanPropertyMatch.sourceToString(), ARROW); + BeanMapperTraceLogger.log("{}{}", beanPropertyMatch.sourceToString(), ARROW); copySourceToTarget(beanPropertyMatch); - logger.debug("{}{}", INDENT, beanPropertyMatch.targetToString()); + BeanMapperTraceLogger.log("{}{}", INDENT, beanPropertyMatch.targetToString()); } } diff --git a/src/main/java/io/beanmapper/strategy/MapToClassStrategy.java b/src/main/java/io/beanmapper/strategy/MapToClassStrategy.java index 26ea0ff..06f45bd 100644 --- a/src/main/java/io/beanmapper/strategy/MapToClassStrategy.java +++ b/src/main/java/io/beanmapper/strategy/MapToClassStrategy.java @@ -4,6 +4,7 @@ import io.beanmapper.config.Configuration; import io.beanmapper.core.BeanMatch; import io.beanmapper.core.converter.BeanConverter; +import io.beanmapper.utils.BeanMapperTraceLogger; public class MapToClassStrategy extends MapToInstanceStrategy { @@ -19,7 +20,7 @@ public T map(S source) { Class valueClass = getConfiguration().getBeanUnproxy().unproxy(source.getClass()); BeanConverter converter = getConverterOptional(valueClass, targetClass); if (converter != null) { - logger.debug("Converter called for source of class {}, while mapping to class {}\t{}->", source.getClass(), targetClass, + BeanMapperTraceLogger.log("Converter called for source of class {}, while mapping to class {}\t{}->", source.getClass(), targetClass, converter.getClass().getSimpleName()); return (T) converter.convert(getBeanMapper(), source, targetClass, null); } diff --git a/src/main/java/io/beanmapper/strategy/MapToDynamicClassStrategy.java b/src/main/java/io/beanmapper/strategy/MapToDynamicClassStrategy.java index 593e7fc..bf7f6ab 100644 --- a/src/main/java/io/beanmapper/strategy/MapToDynamicClassStrategy.java +++ b/src/main/java/io/beanmapper/strategy/MapToDynamicClassStrategy.java @@ -5,6 +5,7 @@ import io.beanmapper.BeanMapper; import io.beanmapper.config.Configuration; +import io.beanmapper.utils.BeanMapperPerformanceLogger; public class MapToDynamicClassStrategy extends AbstractMapStrategy { @@ -48,21 +49,25 @@ public T downsizeSource(S source, List downsizeSourceFields) { Class targetClass = getConfiguration().getTargetClass(); Object target = getConfiguration().getTarget(); - Object dynSource = getBeanMapper() - .wrap() - .downsizeSource(null) - .setTarget(target) - .setTargetClass(dynamicClass) - .build() - .map(source); + Object dynSource = BeanMapperPerformanceLogger.runTimed("Recursively calling BeanMapper#map(Object), to map source of type %s, to target of type %s." + .formatted(source.getClass().getCanonicalName(), dynamicClass.getCanonicalName()), + () -> getBeanMapper() + .wrap() + .downsizeSource(null) + .setTarget(target) + .setTargetClass(dynamicClass) + .build() + .map(source)); - return getBeanMapper() - .wrap() - .downsizeSource(null) - .setTarget(target) - .setTargetClass(targetClass) - .build() - .map(dynSource); + return BeanMapperPerformanceLogger.runTimed("Recursively calling BeanMapper#map(Object), to map source of type %s, to type %s." + .formatted(dynSource.getClass().getCanonicalName(), targetClass != null ? targetClass.getCanonicalName() : "null"), + () -> getBeanMapper() + .wrap() + .downsizeSource(null) + .setTarget(target) + .setTargetClass(targetClass) + .build() + .map(dynSource)); } public T downsizeTarget(S source, List downsizeTargetFields) { @@ -71,12 +76,13 @@ public T downsizeTarget(S source, List downsizeTargetFields) { downsizeTargetFields, getConfiguration().getStrictMappingProperties()); Class collectionClass = getBeanMapper().getConfiguration().getCollectionClass(); - return getBeanMapper() + return BeanMapperPerformanceLogger.runTimed("Recursively calling BeanMapper#map(Object), to map source of type %s, to type %s." + .formatted(source.getClass().getCanonicalName(), dynamicClass.getCanonicalName()), () -> getBeanMapper() .wrap() .downsizeTarget(null) .setCollectionClass(collectionClass) .setTargetClass(dynamicClass) .build() - .map(source); + .map(source)); } } diff --git a/src/main/java/io/beanmapper/strategy/MapToRecordStrategy.java b/src/main/java/io/beanmapper/strategy/MapToRecordStrategy.java index 9a57912..ce35715 100644 --- a/src/main/java/io/beanmapper/strategy/MapToRecordStrategy.java +++ b/src/main/java/io/beanmapper/strategy/MapToRecordStrategy.java @@ -26,6 +26,7 @@ import io.beanmapper.exceptions.RecordConstructorConflictException; import io.beanmapper.exceptions.RecordNoAvailableConstructorsExceptions; import io.beanmapper.exceptions.SourceFieldAccessException; +import io.beanmapper.utils.BeanMapperTraceLogger; import io.beanmapper.utils.Records; /** @@ -56,9 +57,8 @@ public T map(final S source) { if (getConfiguration().isConverterChoosable()) { BeanConverter converter = getConverterOptional(source.getClass(), this.getConfiguration().getTargetClass()); if (converter != null) { - if (logger.isDebugEnabled()) - logger.debug("Converter called for source of class {}, while mapping to class {}\t{}->", source.getClass(), targetClass, - converter.getClass().getSimpleName()); + BeanMapperTraceLogger.log("Converter called for source of class {}, while mapping to class {}\t{}->", source.getClass(), targetClass, + converter.getClass().getSimpleName()); return converter.convert(getBeanMapper(), source, targetClass, null); } } @@ -76,8 +76,8 @@ public T map(final S source) { * present. * * @param sourceClass The class of the source-object. + * @param The type of the sourceClass. * @return A Map containing the fields of the source-class, mapped by the name of the field, or the value of an available BeanAlias. - * @param The type of the sourceClass. */ private Map getFieldsOfClass(final Class sourceClass) { return Arrays.stream(sourceClass.getDeclaredFields()) @@ -95,8 +95,8 @@ private Map getFieldsOfClass(final Class sourceClass) { * BeanAlias-annotation. * * @param source The source-object, of which the fields will be mapped. - * @return The fields of the source-object, mapped to the * @param + * @return The fields of the source-object, mapped to the */ private Map getSourceFields(final S source) { Map sourceFields = new HashMap<>(); @@ -115,8 +115,8 @@ private Map getSourceFields(final S source) { * from an available {@link io.beanmapper.annotations.BeanProperty BeanProperty}-annotation.

* * @param targetClass The class of the target record. + * @param The type of the target record. * @return The names of the RecordComponents as a String-array. - * @param The type of the target record. */ private String[] getNamesOfRecordComponents(final Class targetClass) { return Arrays.stream(targetClass.getRecordComponents()) @@ -137,8 +137,8 @@ private String[] getNamesOfRecordComponents(final Class targetClass) { * * @param targetClass The target record. * @param constructor The target constructor. + * @param The type of the target class. * @return The String-array containing the names of the constructor-parameters. - * @param The type of the target class. */ private String[] getNamesOfConstructorParameters(final Class targetClass, final Constructor constructor) { if (constructor.isAnnotationPresent(BeanRecordConstruct.class)) { @@ -176,7 +176,7 @@ private Object[] getConstructorArgumentsMappedToCorrectTargetType(final Paramete } else { var parameterType = parameters[i].getType(); if (Collection.class.isAssignableFrom(parameterType) || Optional.class.isAssignableFrom(parameterType) - || Map.class.isAssignableFrom(parameterType)) { + || Map.class.isAssignableFrom(parameterType)) { arguments[i] = this.getBeanMapper().map(values.get(i), (ParameterizedType) parameters[i].getParameterizedType()); } else { arguments[i] = values.get(i) != null && parameters[i].getType().equals(values.get(i).getClass()) ? @@ -232,8 +232,8 @@ private Constructor getSuitableConstructor(final Map sourc var recordConstruct = (BeanRecordConstruct) canonicalConstructor.getAnnotation(BeanRecordConstruct.class); if (recordConstruct.constructMode() == BeanRecordConstructMode.EXCLUDE) throw new RecordNoAvailableConstructorsExceptions((Class) targetClass, "All available constructors have been " - + "annotated with @RecordConstruct(constructMode = RecordConstructMode.EXCLUDE). To enable mapping to the target record, please make at " - + "least one constructor available."); + + "annotated with @RecordConstruct(constructMode = RecordConstructMode.EXCLUDE). To enable mapping to the target record, please make at " + + "least one constructor available."); } return canonicalConstructor; } diff --git a/src/main/java/io/beanmapper/utils/BeanMapperPerformanceLogger.java b/src/main/java/io/beanmapper/utils/BeanMapperPerformanceLogger.java new file mode 100644 index 0000000..9f89022 --- /dev/null +++ b/src/main/java/io/beanmapper/utils/BeanMapperPerformanceLogger.java @@ -0,0 +1,44 @@ +package io.beanmapper.utils; + +import java.time.Duration; +import java.time.Instant; +import java.util.function.Supplier; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BeanMapperPerformanceLogger { + + private static final Logger log = LoggerFactory.getLogger(BeanMapperPerformanceLogger.class); + private static final String LOG_TEMPLATE = "Performed operation in {}ms. ({})"; + + public static void runTimed(String taskName, Runnable task) { + Stopwatch stopwatch = Stopwatch.create(); + task.run(); + log.debug(LOG_TEMPLATE, stopwatch.stop(), taskName); + } + + public static T runTimed(String taskName, Supplier task) { + Stopwatch stopwatch = Stopwatch.create(); + T result = task.get(); + log.debug(LOG_TEMPLATE, stopwatch.stop(), taskName); + return result; + } + + private static class Stopwatch { + + private final Instant started; + + Stopwatch() { + this.started = Instant.now(); + } + + public static Stopwatch create() { + return new Stopwatch(); + } + + public double stop() { + return Duration.between(started, Instant.now()).toNanos() / 1_000_000.0; + } + } +} diff --git a/src/main/java/io/beanmapper/utils/BeanMapperTraceLogger.java b/src/main/java/io/beanmapper/utils/BeanMapperTraceLogger.java new file mode 100644 index 0000000..98f2575 --- /dev/null +++ b/src/main/java/io/beanmapper/utils/BeanMapperTraceLogger.java @@ -0,0 +1,17 @@ +package io.beanmapper.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BeanMapperTraceLogger { + + private static final Logger log = LoggerFactory.getLogger(BeanMapperTraceLogger.class); + + public static void log(String message) { + log.trace(message); + } + + public static void log(String message, Object... args) { + log.trace(message, args); + } +} diff --git a/src/main/java/io/beanmapper/utils/Classes.java b/src/main/java/io/beanmapper/utils/Classes.java index ff56792..3f96c98 100644 --- a/src/main/java/io/beanmapper/utils/Classes.java +++ b/src/main/java/io/beanmapper/utils/Classes.java @@ -9,6 +9,9 @@ import io.beanmapper.core.BeanPropertyMatch; import io.beanmapper.exceptions.BeanNoSuchPropertyException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Reflection utilities. * @@ -17,6 +20,8 @@ */ public class Classes { + private static final Logger log = LoggerFactory.getLogger(Classes.class); + /** * Private constructor to hide implicit public constructor of utility-class. */ @@ -62,6 +67,7 @@ public static Type[] getParameteredTypes(Class clazz, BeanPropertyMatch beanP return ((ParameterizedType) clazz.getDeclaredField(beanPropertyMatch.getTargetFieldName()) .getGenericType()).getActualTypeArguments(); } catch (NoSuchFieldException e) { + log.error(e.getMessage()); throw new BeanNoSuchPropertyException(e.getMessage()); } } diff --git a/src/test/java/io/beanmapper/config/BeanMapperBuilderTest.java b/src/test/java/io/beanmapper/config/BeanMapperBuilderTest.java index c50bc21..70a2f40 100644 --- a/src/test/java/io/beanmapper/config/BeanMapperBuilderTest.java +++ b/src/test/java/io/beanmapper/config/BeanMapperBuilderTest.java @@ -19,6 +19,7 @@ import io.beanmapper.strategy.ConstructorArguments; import io.beanmapper.utils.Trinary; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; class BeanMapperBuilderTest { @@ -137,6 +138,7 @@ public T instantiate(Class beanClass, ConstructorArguments arguments) { } @Test + @Disabled("BeanUnproxy should not be used as a Converter.") void setBeanUnproxy() { final BeanUnproxy expectedBeanUnproxy = new BeanUnproxy() { @Override