DATACMNS-867 - First draft.

This commit is contained in:
Oliver Gierke
2016-11-14 20:10:22 +01:00
parent 1b17271915
commit cc63e5b7a4
278 changed files with 4737 additions and 5714 deletions

View File

@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.data.annotation.CreatedBy;
@@ -29,6 +30,7 @@ import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.convert.Jsr310Converters;
import org.springframework.data.convert.ThreeTenBackPortConverters;
import org.springframework.data.util.Optionals;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.data.util.ReflectionUtils.AnnotationFieldFilter;
import org.springframework.util.Assert;
@@ -68,10 +70,10 @@ final class AnnotationAuditingMetadata {
SUPPORTED_DATE_TYPES = Collections.unmodifiableList(types);
}
private final Field createdByField;
private final Field createdDateField;
private final Field lastModifiedByField;
private final Field lastModifiedDateField;
private final Optional<Field> createdByField;
private final Optional<Field> createdDateField;
private final Optional<Field> lastModifiedByField;
private final Optional<Field> lastModifiedDateField;
/**
* Creates a new {@link AnnotationAuditingMetadata} instance for the given type.
@@ -82,10 +84,10 @@ final class AnnotationAuditingMetadata {
Assert.notNull(type, "Given type must not be null!");
this.createdByField = ReflectionUtils.findField(type, CREATED_BY_FILTER);
this.createdDateField = ReflectionUtils.findField(type, CREATED_DATE_FILTER);
this.lastModifiedByField = ReflectionUtils.findField(type, LAST_MODIFIED_BY_FILTER);
this.lastModifiedDateField = ReflectionUtils.findField(type, LAST_MODIFIED_DATE_FILTER);
this.createdByField = Optional.ofNullable(ReflectionUtils.findField(type, CREATED_BY_FILTER));
this.createdDateField = Optional.ofNullable(ReflectionUtils.findField(type, CREATED_DATE_FILTER));
this.lastModifiedByField = Optional.ofNullable(ReflectionUtils.findField(type, LAST_MODIFIED_BY_FILTER));
this.lastModifiedDateField = Optional.ofNullable(ReflectionUtils.findField(type, LAST_MODIFIED_DATE_FILTER));
assertValidDateFieldType(createdDateField);
assertValidDateFieldType(lastModifiedDateField);
@@ -94,23 +96,26 @@ final class AnnotationAuditingMetadata {
/**
* Checks whether the given field has a type that is a supported date type.
*
* @param field can be {@literal null}.
* @param field
*/
private void assertValidDateFieldType(Field field) {
private void assertValidDateFieldType(Optional<Field> field) {
if (field == null || SUPPORTED_DATE_TYPES.contains(field.getType().getName())) {
return;
}
field.ifPresent(it -> {
Class<?> type = field.getType();
if (SUPPORTED_DATE_TYPES.contains(it.getType().getName())) {
return;
}
if (Jsr310Converters.supports(type) || ThreeTenBackPortConverters.supports(type)) {
return;
}
Class<?> type = it.getType();
throw new IllegalStateException(String.format(
"Found created/modified date field with type %s but only %s as well as java.time types are supported!", type,
SUPPORTED_DATE_TYPES));
if (Jsr310Converters.supports(type) || ThreeTenBackPortConverters.supports(type)) {
return;
}
throw new IllegalStateException(String.format(
"Found created/modified date field with type %s but only %s as well as java.time types are supported!", type,
SUPPORTED_DATE_TYPES));
});
}
/**
@@ -119,52 +124,41 @@ final class AnnotationAuditingMetadata {
* @param type the type to inspect, must not be {@literal null}.
*/
public static AnnotationAuditingMetadata getMetadata(Class<?> type) {
if (METADATA_CACHE.containsKey(type)) {
return METADATA_CACHE.get(type);
}
AnnotationAuditingMetadata metadata = new AnnotationAuditingMetadata(type);
METADATA_CACHE.put(type, metadata);
return metadata;
return METADATA_CACHE.computeIfAbsent(type, it -> new AnnotationAuditingMetadata(it));
}
/**
* Returns whether the {@link Class} represented in this instance is auditable or not.
*/
public boolean isAuditable() {
if (createdByField == null && createdDateField == null && lastModifiedByField == null
&& lastModifiedDateField == null) {
return false;
}
return true;
return Optionals.isAnyPresent(createdByField, createdDateField, lastModifiedByField, lastModifiedDateField);
}
/**
* Return the field annotated by {@link CreatedBy}, or {@literal null}.
* Return the field annotated by {@link CreatedBy}.
*/
public Field getCreatedByField() {
public Optional<Field> getCreatedByField() {
return createdByField;
}
/**
* Return the field annotated by {@link CreatedDate}, or {@literal null}.
* Return the field annotated by {@link CreatedDate}.
*/
public Field getCreatedDateField() {
public Optional<Field> getCreatedDateField() {
return createdDateField;
}
/**
* Return the field annotated by {@link LastModifiedBy}, or {@literal null}.
* Return the field annotated by {@link LastModifiedBy}.
*/
public Field getLastModifiedByField() {
public Optional<Field> getLastModifiedByField() {
return lastModifiedByField;
}
/**
* Return the field annotated by {@link LastModifiedDate}, or {@literal null}.
* Return the field annotated by {@link LastModifiedDate}.
*/
public Field getLastModifiedDateField() {
public Optional<Field> getLastModifiedDateField() {
return lastModifiedDateField;
}
}

View File

@@ -15,7 +15,8 @@
*/
package org.springframework.data.auditing;
import java.util.Calendar;
import java.time.temporal.TemporalAccessor;
import java.util.Optional;
/**
* Interface to abstract the ways setting the auditing information can be implemented.
@@ -30,34 +31,34 @@ public interface AuditableBeanWrapper {
*
* @param value
*/
void setCreatedBy(Object value);
Optional<? extends Object> setCreatedBy(Optional<? extends Object> value);
/**
* Set the date the object was created.
*
* @param value
*/
void setCreatedDate(Calendar value);
Optional<TemporalAccessor> setCreatedDate(Optional<TemporalAccessor> value);
/**
* Set the last modifier of the object.
*
* @param value
*/
void setLastModifiedBy(Object value);
Optional<? extends Object> setLastModifiedBy(Optional<? extends Object> value);
/**
* Returns the date of the last modification date of the backing bean.
*
* @return the date of the last modification, can be {@literal null}.
* @return the date of the last modification.
* @since 1.10
*/
Calendar getLastModifiedDate();
Optional<TemporalAccessor> getLastModifiedDate();
/**
* Set the last modification date.
*
* @param value
*/
void setLastModifiedDate(Calendar value);
Optional<TemporalAccessor> setLastModifiedDate(Optional<TemporalAccessor> value);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.auditing;
import java.util.Optional;
/**
* A factory to lookup {@link AuditableBeanWrapper}s.
*
@@ -24,11 +26,10 @@ package org.springframework.data.auditing;
public interface AuditableBeanWrapperFactory {
/**
* Returns the {@link AuditableBeanWrapper} for the given source obejct if it's eligible for auditing.
* Returns the {@link AuditableBeanWrapper} for the given source object if it's eligible for auditing.
*
* @param source can be {@literal null}.
* @return the {@link AuditableBeanWrapper} for the given source obejct if it's eligible for auditing or
* {@literal null} otherwise.
* @param source.
* @return the {@link AuditableBeanWrapper} for the given source object if it's eligible for auditing.
*/
AuditableBeanWrapper getBeanWrapperFor(Object source);
Optional<AuditableBeanWrapper> getBeanWrapperFor(Optional<? extends Object> source);
}

View File

@@ -15,8 +15,9 @@
*/
package org.springframework.data.auditing;
import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Optional;
import org.joda.time.DateTime;
import org.slf4j.Logger;
@@ -43,7 +44,7 @@ public class AuditingHandler implements InitializingBean {
private final DefaultAuditableBeanWrapperFactory factory;
private DateTimeProvider dateTimeProvider = CurrentDateTimeProvider.INSTANCE;
private AuditorAware<?> auditorAware;
private Optional<AuditorAware<?>> auditorAware;
private boolean dateTimeForNow = true;
private boolean modifyOnCreation = true;
@@ -56,7 +57,6 @@ public class AuditingHandler implements InitializingBean {
* @deprecated use {@link AuditingHandler(PersistentEntities)} instead.
*/
@Deprecated
@SuppressWarnings("unchecked")
public AuditingHandler(
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
this(new PersistentEntities(Arrays.asList(mappingContext)));
@@ -72,7 +72,9 @@ public class AuditingHandler implements InitializingBean {
public AuditingHandler(PersistentEntities entities) {
Assert.notNull(entities, "PersistentEntities must not be null!");
this.factory = new MappingAuditableBeanWrapperFactory(entities);
this.auditorAware = Optional.empty();
}
/**
@@ -80,10 +82,10 @@ public class AuditingHandler implements InitializingBean {
*
* @param auditorAware must not be {@literal null}.
*/
public void setAuditorAware(final AuditorAware<?> auditorAware) {
public void setAuditorAware(AuditorAware<?> auditorAware) {
Assert.notNull(auditorAware, "AuditorAware must not be null!");
this.auditorAware = auditorAware;
this.auditorAware = Optional.of(auditorAware);
}
/**
@@ -121,7 +123,7 @@ public class AuditingHandler implements InitializingBean {
*
* @param source
*/
public void markCreated(Object source) {
public void markCreated(Optional<? extends Object> source) {
touch(source, true);
}
@@ -130,35 +132,35 @@ public class AuditingHandler implements InitializingBean {
*
* @param source
*/
public void markModified(Object source) {
public void markModified(Optional<? extends Object> source) {
touch(source, false);
}
/**
* Returns whether the given source is considered to be auditable in the first place
*
* @param source can be {@literal null}.
*
* @param source must not be {@literal null}.
* @return
*/
protected final boolean isAuditable(Object source) {
return factory.getBeanWrapperFor(source) != null;
protected final boolean isAuditable(Optional<Object> source) {
return source.flatMap(o -> factory.getBeanWrapperFor(source)).isPresent();
}
private void touch(Object target, boolean isNew) {
private void touch(Optional<? extends Object> target, boolean isNew) {
AuditableBeanWrapper wrapper = factory.getBeanWrapperFor(target);
Optional<AuditableBeanWrapper> wrapper = factory.getBeanWrapperFor(target);
if (wrapper == null) {
return;
}
wrapper.ifPresent(it -> {
Object auditor = touchAuditor(wrapper, isNew);
Calendar now = dateTimeForNow ? touchDate(wrapper, isNew) : null;
Optional<Object> auditor = touchAuditor(it, isNew);
Optional<TemporalAccessor> now = dateTimeForNow ? touchDate(it, isNew) : Optional.empty();
Object defaultedNow = now == null ? "not set" : now;
Object defaultedAuditor = auditor == null ? "unknown" : auditor;
Object defaultedNow = now.map(Object::toString).orElse("not set");
Object defaultedAuditor = auditor.map(Object::toString).orElse("unknown");
LOGGER.debug("Touched {} - Last modification at {} by {}", new Object[] { target, defaultedNow, defaultedAuditor });
LOGGER.debug("Touched {} - Last modification at {} by {}",
new Object[] { target, defaultedNow, defaultedAuditor });
});
}
/**
@@ -167,23 +169,22 @@ public class AuditingHandler implements InitializingBean {
* @param auditable
* @return
*/
private Object touchAuditor(AuditableBeanWrapper wrapper, boolean isNew) {
private Optional<Object> touchAuditor(AuditableBeanWrapper wrapper, boolean isNew) {
if (null == auditorAware) {
return null;
}
return auditorAware.map(it -> {
Object auditor = auditorAware.getCurrentAuditor();
Optional<?> auditor = it.getCurrentAuditor();
if (isNew) {
wrapper.setCreatedBy(auditor);
if (!modifyOnCreation) {
return auditor;
if (isNew) {
wrapper.setCreatedBy(auditor);
if (!modifyOnCreation) {
return auditor;
}
}
}
wrapper.setLastModifiedBy(auditor);
return auditor;
return wrapper.setLastModifiedBy(auditor);
});
}
/**
@@ -192,9 +193,9 @@ public class AuditingHandler implements InitializingBean {
* @param wrapper
* @return
*/
private Calendar touchDate(AuditableBeanWrapper wrapper, boolean isNew) {
private Optional<TemporalAccessor> touchDate(AuditableBeanWrapper wrapper, boolean isNew) {
Calendar now = dateTimeProvider.getNow();
Optional<TemporalAccessor> now = dateTimeProvider.getNow();
if (isNew) {
wrapper.setCreatedDate(now);
@@ -203,8 +204,7 @@ public class AuditingHandler implements InitializingBean {
}
}
wrapper.setLastModifiedDate(now);
return now;
return wrapper.setLastModifiedDate(now);
}
/*

View File

@@ -15,8 +15,9 @@
*/
package org.springframework.data.auditing;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Optional;
import org.joda.time.DateTime;
@@ -35,7 +36,7 @@ public enum CurrentDateTimeProvider implements DateTimeProvider {
* @see org.springframework.data.auditing.DateTimeProvider#getNow()
*/
@Override
public Calendar getNow() {
return new GregorianCalendar();
public Optional<TemporalAccessor> getNow() {
return Optional.of(ZonedDateTime.now());
}
}

View File

@@ -15,7 +15,8 @@
*/
package org.springframework.data.auditing;
import java.util.Calendar;
import java.time.temporal.TemporalAccessor;
import java.util.Optional;
/**
* SPI to calculate the current time to be used when auditing.
@@ -30,5 +31,5 @@ public interface DateTimeProvider {
*
* @return
*/
Calendar getNow();
Optional<TemporalAccessor> getNow();
}

View File

@@ -15,21 +15,24 @@
*/
package org.springframework.data.auditing;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.lang.reflect.Field;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.Optional;
import org.joda.time.DateTime;
import org.joda.time.LocalDateTime;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.JodaTimeConverters;
import org.springframework.data.convert.Jsr310Converters;
import org.springframework.data.convert.ThreeTenBackPortConverters;
import org.springframework.data.domain.Auditable;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* A factory class to {@link AuditableBeanWrapper} instances.
@@ -46,23 +49,22 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @return
*/
@SuppressWarnings("unchecked")
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
public Optional<AuditableBeanWrapper> getBeanWrapperFor(Optional<? extends Object> source) {
return source.map(it -> {
if (it instanceof Auditable) {
return new AuditableInterfaceBeanWrapper((Auditable<Object, ?, TemporalAccessor>) it);
}
AnnotationAuditingMetadata metadata = AnnotationAuditingMetadata.getMetadata(it.getClass());
if (metadata.isAuditable()) {
return new ReflectionAuditingBeanWrapper(it);
}
if (source == null) {
return null;
}
if (source instanceof Auditable) {
return new AuditableInterfaceBeanWrapper((Auditable<Object, ?>) source);
}
AnnotationAuditingMetadata metadata = AnnotationAuditingMetadata.getMetadata(source.getClass());
if (metadata.isAuditable()) {
return new ReflectionAuditingBeanWrapper(source);
}
return null;
});
}
/**
@@ -70,36 +72,53 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
*
* @author Oliver Gierke
*/
@RequiredArgsConstructor
static class AuditableInterfaceBeanWrapper extends DateConvertingAuditableBeanWrapper {
private final Auditable<Object, ?> auditable;
private final @NonNull Auditable<Object, ?, TemporalAccessor> auditable;
private final Class<? extends TemporalAccessor> type;
@SuppressWarnings("unchecked")
public AuditableInterfaceBeanWrapper(Auditable<Object, ?, TemporalAccessor> auditable) {
public AuditableInterfaceBeanWrapper(Auditable<Object, ?> auditable) {
this.auditable = auditable;
this.type = (Class<? extends TemporalAccessor>) ResolvableType.forClass(Auditable.class, auditable.getClass())
.getGeneric(2).getRawClass();
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.util.Optional)
*/
public void setCreatedBy(Object value) {
@Override
public Optional<? extends Object> setCreatedBy(Optional<? extends Object> value) {
auditable.setCreatedBy(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(org.joda.time.DateTime)
*/
public void setCreatedDate(Calendar value) {
auditable.setCreatedDate(new DateTime(value));
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Optional)
*/
public void setLastModifiedBy(Object value) {
@Override
public Optional<TemporalAccessor> setCreatedDate(Optional<TemporalAccessor> value) {
auditable.setCreatedDate(getAsTemporalAccessor(value, type));
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.DefaultAuditableBeanWrapperFactory.AuditableInterfaceBeanWrapper#setLastModifiedBy(java.util.Optional)
*/
@Override
public Optional<? extends Object> setLastModifiedBy(Optional<? extends Object> value) {
auditable.setLastModifiedBy(value);
return value;
}
/*
@@ -107,16 +126,20 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @see org.springframework.data.auditing.AuditableBeanWrapper#getLastModifiedDate()
*/
@Override
public Calendar getLastModifiedDate() {
return getAsCalendar(auditable.getLastModifiedDate());
public Optional<TemporalAccessor> getLastModifiedDate() {
return getAsTemporalAccessor(auditable.getLastModifiedDate(), TemporalAccessor.class);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(org.joda.time.DateTime)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Optional)
*/
public void setLastModifiedDate(Calendar value) {
auditable.setLastModifiedDate(new DateTime(value));
@Override
public Optional<TemporalAccessor> setLastModifiedDate(Optional<TemporalAccessor> value) {
auditable.setLastModifiedDate(getAsTemporalAccessor(value, type));
return value;
}
}
@@ -129,9 +152,6 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
*/
abstract static class DateConvertingAuditableBeanWrapper implements AuditableBeanWrapper {
private static final boolean IS_JODA_TIME_PRESENT = ClassUtils.isPresent("org.joda.time.DateTime",
ReflectionAuditingBeanWrapper.class.getClassLoader());
private final ConversionService conversionService;
/**
@@ -141,18 +161,9 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
if (IS_JODA_TIME_PRESENT) {
conversionService.addConverter(CalendarToDateTimeConverter.INSTANCE);
conversionService.addConverter(CalendarToLocalDateTimeConverter.INSTANCE);
}
for (Converter<?, ?> converter : Jsr310Converters.getConvertersToRegister()) {
conversionService.addConverter(converter);
}
for (Converter<?, ?> converter : ThreeTenBackPortConverters.getConvertersToRegister()) {
conversionService.addConverter(converter);
}
JodaTimeConverters.getConvertersToRegister().forEach(it -> conversionService.addConverter(it));
Jsr310Converters.getConvertersToRegister().forEach(it -> conversionService.addConverter(it));
ThreeTenBackPortConverters.getConvertersToRegister().forEach(it -> conversionService.addConverter(it));
this.conversionService = conversionService;
}
@@ -165,28 +176,27 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @param source must not be {@literal null}.
* @return
*/
protected Object getDateValueToSet(Calendar value, Class<?> targetType, Object source) {
protected Optional<Object> getDateValueToSet(Optional<TemporalAccessor> value, Class<?> targetType, Object source) {
if (value == null) {
return null;
}
return value.map(it -> {
if (Calendar.class.equals(targetType)) {
return value;
}
if (TemporalAccessor.class.equals(targetType)) {
return it;
}
if (conversionService.canConvert(Calendar.class, targetType)) {
return conversionService.convert(value, targetType);
}
if (conversionService.canConvert(it.getClass(), targetType)) {
return conversionService.convert(it, targetType);
}
if (conversionService.canConvert(Date.class, targetType)) {
if (conversionService.canConvert(Date.class, targetType)) {
Date date = conversionService.convert(value, Date.class);
return conversionService.convert(date, targetType);
}
Date date = conversionService.convert(it, Date.class);
return conversionService.convert(date, targetType);
}
throw new IllegalArgumentException(String.format("Invalid date type for member %s! Supported types are %s.",
source, AnnotationAuditingMetadata.SUPPORTED_DATE_TYPES));
throw new IllegalArgumentException(String.format("Invalid date type for member %s! Supported types are %s.",
source, AnnotationAuditingMetadata.SUPPORTED_DATE_TYPES));
});
}
/**
@@ -195,22 +205,17 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @param source can be {@literal null}.
* @return
*/
protected Calendar getAsCalendar(Object source) {
@SuppressWarnings("unchecked")
protected <T> Optional<T> getAsTemporalAccessor(Optional<?> source, Class<T> target) {
if (source == null || source instanceof Calendar) {
return (Calendar) source;
}
// Apply conversion to date if necessary and possible
source = !(source instanceof Date) && conversionService.canConvert(source.getClass(), Date.class) ? conversionService
.convert(source, Date.class) : source;
return conversionService.convert(source, Calendar.class);
return source.map(it -> {
return target.isInstance(it) ? (T) it : conversionService.convert(it, target);
});
}
}
/**
* An {@link AuditableBeanWrapper} implementation that sets values on the target object using refelction.
* An {@link AuditableBeanWrapper} implementation that sets values on the target object using reflection.
*
* @author Oliver Gierke
*/
@@ -234,26 +239,29 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.util.Optional)
*/
public void setCreatedBy(Object value) {
setField(metadata.getCreatedByField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Calendar)
*/
public void setCreatedDate(Calendar value) {
setDateField(metadata.getCreatedDateField(), value);
@Override
public Optional<? extends Object> setCreatedBy(Optional<? extends Object> value) {
return setField(metadata.getCreatedByField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Optional)
*/
public void setLastModifiedBy(Object value) {
setField(metadata.getLastModifiedByField(), value);
@Override
public Optional<TemporalAccessor> setCreatedDate(Optional<TemporalAccessor> value) {
return setDateField(metadata.getCreatedDateField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.util.Optional)
*/
@Override
public Optional<? extends Object> setLastModifiedBy(Optional<? extends Object> value) {
return setField(metadata.getLastModifiedByField(), value);
}
/*
@@ -261,18 +269,39 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @see org.springframework.data.auditing.AuditableBeanWrapper#getLastModifiedDate()
*/
@Override
public Calendar getLastModifiedDate() {
public Optional<TemporalAccessor> getLastModifiedDate() {
return getAsCalendar(org.springframework.util.ReflectionUtils.getField(metadata.getLastModifiedDateField(),
target));
return getAsTemporalAccessor(metadata.getLastModifiedDateField().map(field -> {
Object value = org.springframework.util.ReflectionUtils.getField(field, target);
return Optional.class.isInstance(value) ? ((Optional<?>) value).orElse(null) : value;
}), TemporalAccessor.class);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Optional)
*/
public void setLastModifiedDate(Calendar value) {
setDateField(metadata.getLastModifiedDateField(), value);
@Override
public Optional<TemporalAccessor> setLastModifiedDate(Optional<TemporalAccessor> value) {
return setDateField(metadata.getLastModifiedDateField(), value);
}
/**
* Sets the given field to the given value if present.
*
* @param field
* @param value
*/
private Optional<? extends Object> setField(Optional<Field> field, Optional<? extends Object> value) {
field.ifPresent(it -> {
ReflectionUtils.setField(it, target,
Optional.class.isAssignableFrom(it.getType()) ? value : value.orElse(null));
});
return value;
}
/**
@@ -281,46 +310,15 @@ class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory
* @param field
* @param value
*/
private void setField(Field field, Object value) {
private Optional<TemporalAccessor> setDateField(Optional<Field> field, Optional<TemporalAccessor> value) {
if (field != null) {
ReflectionUtils.setField(field, target, value);
}
}
field.ifPresent(it -> {
Optional<Object> toSet = getDateValueToSet(value, it.getType(), it);
ReflectionUtils.setField(it, target,
Optional.class.isAssignableFrom(it.getType()) ? toSet : toSet.orElse(null));
});
/**
* Sets the given field to the given value if the field is not {@literal null}.
*
* @param field
* @param value
*/
private void setDateField(Field field, Calendar value) {
if (field == null) {
return;
}
ReflectionUtils.setField(field, target, getDateValueToSet(value, field.getType(), field));
}
}
private static enum CalendarToDateTimeConverter implements Converter<Calendar, DateTime> {
INSTANCE;
@Override
public DateTime convert(Calendar source) {
return new DateTime(source);
}
}
private static enum CalendarToLocalDateTimeConverter implements Converter<Calendar, LocalDateTime> {
INSTANCE;
@Override
public LocalDateTime convert(Calendar source) {
return new LocalDateTime(source);
return value;
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.auditing;
import java.util.Arrays;
import java.util.Optional;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
@@ -24,11 +25,12 @@ import org.springframework.data.mapping.context.MappingContextIsNewStrategyFacto
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.support.IsNewStrategy;
import org.springframework.data.support.IsNewStrategyFactory;
import org.springframework.util.Assert;
/**
* {@link AuditingHandler} extension that uses an {@link IsNewStrategyFactory} to expose a generic
* {@link #markAudited(Object)} method that will route calls to {@link #markCreated(Object)} or
* {@link #markModified(Object)} based on the {@link IsNewStrategy} determined from the factory.
* {@link #markAudited(Optional)} method that will route calls to {@link #markCreated(Optional)} or
* {@link #markModified(Optional)} based on the {@link IsNewStrategy} determined from the factory.
*
* @author Oliver Gierke
* @since 1.5
@@ -45,7 +47,6 @@ public class IsNewAwareAuditingHandler extends AuditingHandler {
* @deprecated use {@link IsNewAwareAuditingHandler(PersistentEntities)} instead.
*/
@Deprecated
@SuppressWarnings("unchecked")
public IsNewAwareAuditingHandler(
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
this(new PersistentEntities(Arrays.asList(mappingContext)));
@@ -54,7 +55,7 @@ public class IsNewAwareAuditingHandler extends AuditingHandler {
/**
* Creates a new {@link IsNewAwareAuditingHandler} for the given {@link MappingContext}.
*
* @param mappingContext must not be {@literal null}.
* @param entities must not be {@literal null}.
* @since 1.10
*/
public IsNewAwareAuditingHandler(PersistentEntities entities) {
@@ -66,23 +67,28 @@ public class IsNewAwareAuditingHandler extends AuditingHandler {
/**
* Marks the given object created or modified based on the {@link IsNewStrategy} returned by the
* {@link IsNewStrategyFactory} configured. Will rout the calls to {@link #markCreated(Object)} and
* {@link #markModified(Object)} accordingly.
* {@link IsNewStrategyFactory} configured. Will rout the calls to {@link #markCreated(Optional)} and
* {@link #markModified(Optional)} accordingly.
*
* @param object
*/
public void markAudited(Object object) {
public void markAudited(Optional<Object> object) {
Assert.notNull(object, "Source object must not be null!");
if (!isAuditable(object)) {
return;
}
IsNewStrategy strategy = isNewStrategyFactory.getIsNewStrategy(object.getClass());
object.ifPresent(it -> {
if (strategy.isNew(object)) {
markCreated(object);
} else {
markModified(object);
}
IsNewStrategy strategy = isNewStrategyFactory.getIsNewStrategy(it.getClass());
if (strategy.isNew(object)) {
markCreated(object);
} else {
markModified(object);
}
});
}
}

View File

@@ -15,9 +15,10 @@
*/
package org.springframework.data.auditing;
import java.util.Calendar;
import java.time.temporal.TemporalAccessor;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
@@ -29,6 +30,7 @@ import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.util.Optionals;
import org.springframework.util.Assert;
/**
@@ -62,32 +64,27 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
* @see org.springframework.data.auditing.AuditableBeanWrapperFactory#getBeanWrapperFor(java.lang.Object)
*/
@Override
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
public Optional<AuditableBeanWrapper> getBeanWrapperFor(Optional<? extends Object> source) {
if (source == null) {
return null;
}
return source.flatMap(it -> {
if (source instanceof Auditable) {
return super.getBeanWrapperFor(source);
}
if (it instanceof Auditable) {
return super.getBeanWrapperFor(source);
}
Class<?> type = source.getClass();
PersistentEntity<?, ?> entity = entities.getPersistentEntity(type);
Class<?> type = it.getClass();
PersistentEntity<?, ?> entity = entities.getPersistentEntity(type);
if (entity == null) {
return super.getBeanWrapperFor(source);
}
if (entity == null) {
return super.getBeanWrapperFor(source);
}
MappingAuditingMetadata metadata = metadataCache.get(type);
MappingAuditingMetadata metadata = metadataCache.computeIfAbsent(type,
foo -> new MappingAuditingMetadata(entity));
if (metadata == null) {
metadata = new MappingAuditingMetadata(entity);
metadataCache.put(type, metadata);
}
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(source);
return metadata.isAuditable() ? new MappingMetadataAuditableBeanWrapper(accessor, metadata) : null;
return Optional.ofNullable(metadata.isAuditable()
? new MappingMetadataAuditableBeanWrapper(entity.getPropertyAccessor(it), metadata) : null);
});
}
/**
@@ -98,8 +95,8 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
*/
static class MappingAuditingMetadata {
private final PersistentProperty<?> createdByProperty, createdDateProperty, lastModifiedByProperty,
lastModifiedDateProperty;
private final Optional<? extends PersistentProperty<?>> createdByProperty, createdDateProperty,
lastModifiedByProperty, lastModifiedDateProperty;
/**
* Creates a new {@link MappingAuditingMetadata} instance from the given {@link PersistentEntity}.
@@ -123,8 +120,8 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
* @return
*/
public boolean isAuditable() {
return createdByProperty != null || createdDateProperty != null || lastModifiedByProperty != null
|| lastModifiedDateProperty != null;
return Optionals.isAnyPresent(createdByProperty, createdDateProperty, lastModifiedByProperty,
lastModifiedDateProperty);
}
}
@@ -158,40 +155,44 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.util.Optional)
*/
@Override
public void setCreatedBy(Object value) {
public Optional<? extends Object> setCreatedBy(Optional<? extends Object> value) {
if (metadata.createdByProperty != null) {
this.accessor.setProperty(metadata.createdByProperty, value);
}
metadata.createdByProperty.ifPresent(it -> {
this.accessor.setProperty(it, value);
});
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Calendar)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(java.util.Optional)
*/
@Override
public void setCreatedDate(Calendar value) {
public Optional<TemporalAccessor> setCreatedDate(Optional<TemporalAccessor> value) {
PersistentProperty<?> property = metadata.createdDateProperty;
metadata.createdDateProperty.ifPresent(it -> {
this.accessor.setProperty(it, getDateValueToSet(value, it.getType(), it));
});
if (property != null) {
this.accessor.setProperty(property, getDateValueToSet(value, property.getType(), property));
}
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.util.Optional)
*/
@Override
public void setLastModifiedBy(Object value) {
public Optional<? extends Object> setLastModifiedBy(Optional<? extends Object> value) {
if (metadata.lastModifiedByProperty != null) {
this.accessor.setProperty(metadata.lastModifiedByProperty, value);
}
metadata.lastModifiedByProperty.ifPresent(it -> {
this.accessor.setProperty(it, value);
});
return value;
}
/*
@@ -199,29 +200,23 @@ public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrap
* @see org.springframework.data.auditing.AuditableBeanWrapper#getLastModifiedDate()
*/
@Override
public Calendar getLastModifiedDate() {
PersistentProperty<?> property = metadata.lastModifiedDateProperty;
if (property == null) {
return null;
}
return getAsCalendar(accessor.getProperty(property));
public Optional<TemporalAccessor> getLastModifiedDate() {
return getAsTemporalAccessor(metadata.lastModifiedDateProperty.map(it -> accessor.getProperty(it)),
TemporalAccessor.class);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Optional)
*/
@Override
public void setLastModifiedDate(Calendar value) {
public Optional<TemporalAccessor> setLastModifiedDate(Optional<TemporalAccessor> value) {
PersistentProperty<?> property = metadata.lastModifiedDateProperty;
metadata.lastModifiedDateProperty.ifPresent(it -> {
this.accessor.setProperty(it, getDateValueToSet(value, it.getType(), it));
});
if (property != null) {
this.accessor.setProperty(property, getDateValueToSet(value, property.getType(), property));
}
return value;
}
}
}