DATACMNS-638, DATACMNS-643 - Support for JSR-310 and ThreeTen datetime types.
Extend AuditableBeanWrapper to allow access to the last modification date of a target object. Made AuditableBeanWrapperFactory an interface and renamed what’s been previously known under this name as DefaultAuditableBeanWrapperFactory. The components previously relying on a MappingContext to lookup a PersistentEntity now use PersistentEntities to be able to back a collection of MappingContexts behind that and also avoid unmanaged types to be added to the MappingContext. We now also register the JSR-310 and ThreeTen back-port converters with the ConversionService to be able to get and set auditing dates as these types.
This commit is contained in:
@@ -27,6 +27,8 @@ import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
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.ReflectionUtils;
|
||||
import org.springframework.data.util.ReflectionUtils.AnnotationFieldFilter;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -93,7 +95,7 @@ final class AnnotationAuditingMetadata {
|
||||
/**
|
||||
* Checks whether the given field has a type that is a supported date type.
|
||||
*
|
||||
* @param field
|
||||
* @param field can be {@literal null}.
|
||||
*/
|
||||
private void assertValidDateFieldType(Field field) {
|
||||
|
||||
@@ -101,14 +103,15 @@ final class AnnotationAuditingMetadata {
|
||||
return;
|
||||
}
|
||||
|
||||
// Support JDK 8 date types if runtime allows
|
||||
if (IS_JDK_8 && field.getType().getPackage().getName().startsWith(JDK8_TIME_PACKAGE_PREFIX)) {
|
||||
Class<?> type = field.getType();
|
||||
|
||||
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!",
|
||||
field.getType(), SUPPORTED_DATE_TYPES));
|
||||
"Found created/modified date field with type %s but only %s as well as java.time types are supported!", type,
|
||||
SUPPORTED_DATE_TYPES));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,6 +46,14 @@ public interface AuditableBeanWrapper {
|
||||
*/
|
||||
void setLastModifiedBy(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}.
|
||||
* @since 1.10
|
||||
*/
|
||||
Calendar getLastModifiedDate();
|
||||
|
||||
/**
|
||||
* Set the last modification date.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,256 +15,20 @@
|
||||
*/
|
||||
package org.springframework.data.auditing;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
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.
|
||||
* A factory to lookup {@link AuditableBeanWrapper}s.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.5
|
||||
* @since 1.10
|
||||
*/
|
||||
class AuditableBeanWrapperFactory {
|
||||
public interface AuditableBeanWrapperFactory {
|
||||
|
||||
/**
|
||||
* Returns an {@link AuditableBeanWrapper} if the given object is capable of being equipped with auditing information.
|
||||
* Returns the {@link AuditableBeanWrapper} for the given source obejct if it's eligible for auditing.
|
||||
*
|
||||
* @param source the auditing candidate.
|
||||
* @return
|
||||
* @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.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link AuditableBeanWrapper} that works with objects implementing
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class AuditableInterfaceBeanWrapper implements AuditableBeanWrapper {
|
||||
|
||||
private final Auditable<Object, ?> auditable;
|
||||
|
||||
public AuditableInterfaceBeanWrapper(Auditable<Object, ?> auditable) {
|
||||
this.auditable = auditable;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
|
||||
*/
|
||||
public void setCreatedBy(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));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
|
||||
*/
|
||||
public void setLastModifiedBy(Object value) {
|
||||
auditable.setLastModifiedBy(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(org.joda.time.DateTime)
|
||||
*/
|
||||
public void setLastModifiedDate(Calendar value) {
|
||||
auditable.setLastModifiedDate(new DateTime(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for {@link AuditableBeanWrapper} implementations that might need to convert {@link Calendar} values into
|
||||
* compatible types when setting date/time information.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.8
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DateConvertingAuditableBeanWrapper}.
|
||||
*/
|
||||
public DateConvertingAuditableBeanWrapper() {
|
||||
|
||||
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
|
||||
|
||||
if (IS_JODA_TIME_PRESENT) {
|
||||
conversionService.addConverter(CalendarToDateTimeConverter.INSTANCE);
|
||||
conversionService.addConverter(CalendarToLocalDateTimeConverter.INSTANCE);
|
||||
}
|
||||
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Calendar} in a type, compatible to the given field.
|
||||
*
|
||||
* @param value can be {@literal null}.
|
||||
* @param targetType must not be {@literal null}.
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected Object getDateValueToSet(Calendar value, Class<?> targetType, Object source) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Calendar.class.equals(targetType)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (conversionService.canConvert(Calendar.class, targetType)) {
|
||||
return conversionService.convert(value, targetType);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Invalid date type for member %s! Supported types are %s.",
|
||||
source, AnnotationAuditingMetadata.SUPPORTED_DATE_TYPES));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link AuditableBeanWrapper} implementation that sets values on the target object using refelction.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class ReflectionAuditingBeanWrapper extends DateConvertingAuditableBeanWrapper {
|
||||
|
||||
private final AnnotationAuditingMetadata metadata;
|
||||
private final Object target;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReflectionAuditingBeanWrapper} to set auditing data on the given target object.
|
||||
*
|
||||
* @param target must not be {@literal null}.
|
||||
*/
|
||||
public ReflectionAuditingBeanWrapper(Object target) {
|
||||
|
||||
Assert.notNull(target, "Target object must not be null!");
|
||||
|
||||
this.metadata = AnnotationAuditingMetadata.getMetadata(target.getClass());
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
|
||||
*/
|
||||
public void setLastModifiedBy(Object value) {
|
||||
setField(metadata.getLastModifiedByField(), value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)
|
||||
*/
|
||||
public void setLastModifiedDate(Calendar value) {
|
||||
setDateField(metadata.getLastModifiedDateField(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given field to the given value if the field is not {@literal null}.
|
||||
*
|
||||
* @param field
|
||||
* @param value
|
||||
*/
|
||||
private void setField(Field field, Object value) {
|
||||
|
||||
if (field != null) {
|
||||
ReflectionUtils.setField(field, target, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
AuditableBeanWrapper getBeanWrapperFor(Object source);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.auditing;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
@@ -26,6 +27,7 @@ import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentEntities;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -38,7 +40,7 @@ public class AuditingHandler implements InitializingBean {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AuditingHandler.class);
|
||||
|
||||
private final AuditableBeanWrapperFactory factory;
|
||||
private final DefaultAuditableBeanWrapperFactory factory;
|
||||
|
||||
private DateTimeProvider dateTimeProvider = CurrentDateTimeProvider.INSTANCE;
|
||||
private AuditorAware<?> auditorAware;
|
||||
@@ -51,12 +53,26 @@ public class AuditingHandler implements InitializingBean {
|
||||
*
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
* @since 1.8
|
||||
* @deprecated use {@link AuditingHandler(PersistentEntities)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
public AuditingHandler(
|
||||
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
|
||||
this(new PersistentEntities(Arrays.asList(mappingContext)));
|
||||
}
|
||||
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
this.factory = new MappingAuditableBeanWrapperFactory(mappingContext);
|
||||
/**
|
||||
* Creates a new {@link AuditableBeanWrapper} using the given {@link PersistentEntities} when looking up auditing
|
||||
* metadata via reflection.
|
||||
*
|
||||
* @param entities must not be {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public AuditingHandler(PersistentEntities entities) {
|
||||
|
||||
Assert.notNull(entities, "PersistentEntities must not be null!");
|
||||
this.factory = new MappingAuditableBeanWrapperFactory(entities);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.auditing;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
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.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.5
|
||||
*/
|
||||
class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory {
|
||||
|
||||
/**
|
||||
* Returns an {@link AuditableBeanWrapper} if the given object is capable of being equipped with auditing information.
|
||||
*
|
||||
* @param source the auditing candidate.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link AuditableBeanWrapper} that works with objects implementing
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class AuditableInterfaceBeanWrapper extends DateConvertingAuditableBeanWrapper {
|
||||
|
||||
private final Auditable<Object, ?> auditable;
|
||||
|
||||
public AuditableInterfaceBeanWrapper(Auditable<Object, ?> auditable) {
|
||||
this.auditable = auditable;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
|
||||
*/
|
||||
public void setCreatedBy(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));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
|
||||
*/
|
||||
public void setLastModifiedBy(Object value) {
|
||||
auditable.setLastModifiedBy(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#getLastModifiedDate()
|
||||
*/
|
||||
@Override
|
||||
public Calendar getLastModifiedDate() {
|
||||
return getAsCalendar(auditable.getLastModifiedDate());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(org.joda.time.DateTime)
|
||||
*/
|
||||
public void setLastModifiedDate(Calendar value) {
|
||||
auditable.setLastModifiedDate(new DateTime(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for {@link AuditableBeanWrapper} implementations that might need to convert {@link Calendar} values into
|
||||
* compatible types when setting date/time information.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.8
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DateConvertingAuditableBeanWrapper}.
|
||||
*/
|
||||
public DateConvertingAuditableBeanWrapper() {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Calendar} in a type, compatible to the given field.
|
||||
*
|
||||
* @param value can be {@literal null}.
|
||||
* @param targetType must not be {@literal null}.
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected Object getDateValueToSet(Calendar value, Class<?> targetType, Object source) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Calendar.class.equals(targetType)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (conversionService.canConvert(Calendar.class, targetType)) {
|
||||
return conversionService.convert(value, targetType);
|
||||
}
|
||||
|
||||
if (conversionService.canConvert(Date.class, targetType)) {
|
||||
|
||||
Date date = conversionService.convert(value, 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given object as {@link Calendar}.
|
||||
*
|
||||
* @param source can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected Calendar getAsCalendar(Object source) {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link AuditableBeanWrapper} implementation that sets values on the target object using refelction.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class ReflectionAuditingBeanWrapper extends DateConvertingAuditableBeanWrapper {
|
||||
|
||||
private final AnnotationAuditingMetadata metadata;
|
||||
private final Object target;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReflectionAuditingBeanWrapper} to set auditing data on the given target object.
|
||||
*
|
||||
* @param target must not be {@literal null}.
|
||||
*/
|
||||
public ReflectionAuditingBeanWrapper(Object target) {
|
||||
|
||||
Assert.notNull(target, "Target object must not be null!");
|
||||
|
||||
this.metadata = AnnotationAuditingMetadata.getMetadata(target.getClass());
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
|
||||
*/
|
||||
public void setLastModifiedBy(Object value) {
|
||||
setField(metadata.getLastModifiedByField(), value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#getLastModifiedDate()
|
||||
*/
|
||||
@Override
|
||||
public Calendar getLastModifiedDate() {
|
||||
|
||||
return getAsCalendar(org.springframework.util.ReflectionUtils.getField(metadata.getLastModifiedDateField(),
|
||||
target));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)
|
||||
*/
|
||||
public void setLastModifiedDate(Calendar value) {
|
||||
setDateField(metadata.getLastModifiedDateField(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given field to the given value if the field is not {@literal null}.
|
||||
*
|
||||
* @param field
|
||||
* @param value
|
||||
*/
|
||||
private void setField(Field field, Object value) {
|
||||
|
||||
if (field != null) {
|
||||
ReflectionUtils.setField(field, target, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,13 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.auditing;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory;
|
||||
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
|
||||
@@ -40,14 +42,26 @@ public class IsNewAwareAuditingHandler extends AuditingHandler {
|
||||
*
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
* @since 1.8
|
||||
* @deprecated use {@link IsNewAwareAuditingHandler(PersistentEntities)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
public IsNewAwareAuditingHandler(
|
||||
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
|
||||
this(new PersistentEntities(Arrays.asList(mappingContext)));
|
||||
}
|
||||
|
||||
super(mappingContext);
|
||||
/**
|
||||
* Creates a new {@link IsNewAwareAuditingHandler} for the given {@link MappingContext}.
|
||||
*
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public IsNewAwareAuditingHandler(PersistentEntities entities) {
|
||||
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
this.isNewStrategyFactory = new MappingContextIsNewStrategyFactory(mappingContext);
|
||||
super(entities);
|
||||
|
||||
this.isNewStrategyFactory = new MappingContextIsNewStrategyFactory(entities);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,6 +28,7 @@ import org.springframework.data.mapping.PersistentEntity;
|
||||
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.mapping.model.BeanWrapper;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -39,22 +40,21 @@ import org.springframework.util.Assert;
|
||||
* @author Oliver Gierke
|
||||
* @since 1.8
|
||||
*/
|
||||
class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory {
|
||||
public class MappingAuditableBeanWrapperFactory extends DefaultAuditableBeanWrapperFactory {
|
||||
|
||||
private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext;
|
||||
private final PersistentEntities entities;
|
||||
private final Map<Class<?>, MappingAuditingMetadata> metadataCache;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappingAuditableBeanWrapperFactory} using the given {@link MappingContext}.
|
||||
* Creates a new {@link MappingAuditableBeanWrapperFactory} using the given {@link PersistentEntities}.
|
||||
*
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
* @param entities must not be {@literal null}.
|
||||
*/
|
||||
public MappingAuditableBeanWrapperFactory(
|
||||
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext) {
|
||||
public MappingAuditableBeanWrapperFactory(PersistentEntities entities) {
|
||||
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
Assert.notNull(entities, "PersistentEntities must not be null!");
|
||||
|
||||
this.mappingContext = mappingContext;
|
||||
this.entities = entities;
|
||||
this.metadataCache = new HashMap<Class<?>, MappingAuditingMetadata>();
|
||||
}
|
||||
|
||||
@@ -65,12 +65,16 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory {
|
||||
@Override
|
||||
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
|
||||
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (source instanceof Auditable) {
|
||||
return super.getBeanWrapperFor(source);
|
||||
}
|
||||
|
||||
Class<?> type = source.getClass();
|
||||
PersistentEntity<?, ?> entity = mappingContext.getPersistentEntity(type);
|
||||
PersistentEntity<?, ?> entity = entities.getPersistentEntity(type);
|
||||
|
||||
if (entity == null) {
|
||||
return super.getBeanWrapperFor(source);
|
||||
@@ -191,6 +195,22 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @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));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(java.util.Calendar)
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -66,6 +67,16 @@ public abstract class Jsr310Converters {
|
||||
return converters;
|
||||
}
|
||||
|
||||
public static boolean supports(Class<?> type) {
|
||||
|
||||
if (!JAVA_8_IS_PRESENT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.<Class<?>> asList(LocalDateTime.class, LocalDate.class, LocalTime.class, Instant.class)
|
||||
.contains(type);
|
||||
}
|
||||
|
||||
public static enum DateToLocalDateTimeConverter implements Converter<Date, LocalDateTime> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.threeten.bp.LocalDateTime.*;
|
||||
import static org.threeten.bp.ZoneId.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -70,6 +71,16 @@ public abstract class ThreeTenBackPortConverters {
|
||||
return converters;
|
||||
}
|
||||
|
||||
public static boolean supports(Class<?> type) {
|
||||
|
||||
if (!THREE_TEN_BACK_PORT_IS_PRESENT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.<Class<?>> asList(LocalDateTime.class, LocalDate.class, LocalTime.class, Instant.class)
|
||||
.contains(type);
|
||||
}
|
||||
|
||||
public static enum DateToLocalDateTimeConverter implements Converter<Date, LocalDateTime> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.mapping.context;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
@@ -34,17 +36,30 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupport {
|
||||
|
||||
private final MappingContext<? extends PersistentEntity<?, ?>, ?> context;
|
||||
private final PersistentEntities context;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MappingContextIsNewStrategyFactory} using the given {@link MappingContext}.
|
||||
*
|
||||
* @param context must not be {@literal null}.
|
||||
* @deprecated use {@link MappingContextIsNewStrategyFactory(PersistentEntities)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
public MappingContextIsNewStrategyFactory(MappingContext<? extends PersistentEntity<?, ?>, ?> context) {
|
||||
this(new PersistentEntities(Arrays.asList(context)));
|
||||
}
|
||||
|
||||
Assert.notNull(context, "MappingContext must not be null!");
|
||||
this.context = context;
|
||||
/**
|
||||
* Creates a new {@link MappingContextIsNewStrategyFactory} using the given {@link PersistentEntities}.
|
||||
*
|
||||
* @param context must not be {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public MappingContextIsNewStrategyFactory(PersistentEntities entities) {
|
||||
|
||||
Assert.notNull(entities, "PersistentEntities must not be null!");
|
||||
this.context = entities;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user