From 0c65c7bb67d507d0c0cf710a5220dc8c4226852d Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 4 Feb 2015 19:30:02 +0100 Subject: [PATCH] DATACMNS-638, DATACMNS-643 - Support for JSR-310 and ThreeTen datetime types. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../auditing/AnnotationAuditingMetadata.java | 13 +- .../data/auditing/AuditableBeanWrapper.java | 10 +- .../auditing/AuditableBeanWrapperFactory.java | 254 +------------- .../data/auditing/AuditingHandler.java | 24 +- .../DefaultAuditableBeanWrapperFactory.java | 326 ++++++++++++++++++ .../auditing/IsNewAwareAuditingHandler.java | 24 +- .../MappingAuditableBeanWrapperFactory.java | 40 ++- .../data/convert/Jsr310Converters.java | 11 + .../convert/ThreeTenBackPortConverters.java | 11 + .../MappingContextIsNewStrategyFactory.java | 23 +- .../auditing/AuditingHandlerUnitTests.java | 9 +- ...AuditableBeanWrapperFactoryUnitTests.java} | 31 +- .../IsNewAwareAuditingHandlerUnitTests.java | 27 +- .../auditing/Jsr310ThreeTenBpAuditedUser.java | 30 ++ ...gAuditableBeanWrapperFactoryUnitTests.java | 97 +++++- ...eflectionAuditingBeanWrapperUnitTests.java | 4 +- ...gContextIsNewStrategyFactoryUnitTests.java | 35 +- 17 files changed, 656 insertions(+), 313 deletions(-) create mode 100644 src/main/java/org/springframework/data/auditing/DefaultAuditableBeanWrapperFactory.java rename src/test/java/org/springframework/data/auditing/{AuditableBeanWrapperFactoryUnitTests.java => DefaultAuditableBeanWrapperFactoryUnitTests.java} (61%) create mode 100644 src/test/java/org/springframework/data/auditing/Jsr310ThreeTenBpAuditedUser.java diff --git a/src/main/java/org/springframework/data/auditing/AnnotationAuditingMetadata.java b/src/main/java/org/springframework/data/auditing/AnnotationAuditingMetadata.java index 69319ac26..66c25b597 100644 --- a/src/main/java/org/springframework/data/auditing/AnnotationAuditingMetadata.java +++ b/src/main/java/org/springframework/data/auditing/AnnotationAuditingMetadata.java @@ -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)); } /** diff --git a/src/main/java/org/springframework/data/auditing/AuditableBeanWrapper.java b/src/main/java/org/springframework/data/auditing/AuditableBeanWrapper.java index 8003a4e05..6514bca3c 100644 --- a/src/main/java/org/springframework/data/auditing/AuditableBeanWrapper.java +++ b/src/main/java/org/springframework/data/auditing/AuditableBeanWrapper.java @@ -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. * diff --git a/src/main/java/org/springframework/data/auditing/AuditableBeanWrapperFactory.java b/src/main/java/org/springframework/data/auditing/AuditableBeanWrapperFactory.java index e90bf799d..c9abf0c25 100644 --- a/src/main/java/org/springframework/data/auditing/AuditableBeanWrapperFactory.java +++ b/src/main/java/org/springframework/data/auditing/AuditableBeanWrapperFactory.java @@ -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) 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 auditable; - - public AuditableInterfaceBeanWrapper(Auditable 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 { - - INSTANCE; - - @Override - public DateTime convert(Calendar source) { - return new DateTime(source); - } - } - - private static enum CalendarToLocalDateTimeConverter implements Converter { - - INSTANCE; - - @Override - public LocalDateTime convert(Calendar source) { - return new LocalDateTime(source); - } - } + AuditableBeanWrapper getBeanWrapperFor(Object source); } diff --git a/src/main/java/org/springframework/data/auditing/AuditingHandler.java b/src/main/java/org/springframework/data/auditing/AuditingHandler.java index 2b7c7a8aa..f7433a51e 100644 --- a/src/main/java/org/springframework/data/auditing/AuditingHandler.java +++ b/src/main/java/org/springframework/data/auditing/AuditingHandler.java @@ -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 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); } /** diff --git a/src/main/java/org/springframework/data/auditing/DefaultAuditableBeanWrapperFactory.java b/src/main/java/org/springframework/data/auditing/DefaultAuditableBeanWrapperFactory.java new file mode 100644 index 000000000..c2f264481 --- /dev/null +++ b/src/main/java/org/springframework/data/auditing/DefaultAuditableBeanWrapperFactory.java @@ -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) 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 auditable; + + public AuditableInterfaceBeanWrapper(Auditable 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 { + + INSTANCE; + + @Override + public DateTime convert(Calendar source) { + return new DateTime(source); + } + } + + private static enum CalendarToLocalDateTimeConverter implements Converter { + + INSTANCE; + + @Override + public LocalDateTime convert(Calendar source) { + return new LocalDateTime(source); + } + } +} diff --git a/src/main/java/org/springframework/data/auditing/IsNewAwareAuditingHandler.java b/src/main/java/org/springframework/data/auditing/IsNewAwareAuditingHandler.java index 401c8b4bc..f9b1d4c59 100644 --- a/src/main/java/org/springframework/data/auditing/IsNewAwareAuditingHandler.java +++ b/src/main/java/org/springframework/data/auditing/IsNewAwareAuditingHandler.java @@ -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 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); } /** diff --git a/src/main/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactory.java b/src/main/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactory.java index 254905258..d1f65acc4 100644 --- a/src/main/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactory.java +++ b/src/main/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactory.java @@ -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 PersistentProperty> mappingContext; + private final PersistentEntities entities; private final Map, 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 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, 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) diff --git a/src/main/java/org/springframework/data/convert/Jsr310Converters.java b/src/main/java/org/springframework/data/convert/Jsr310Converters.java index 45694283f..52d8bc4f0 100644 --- a/src/main/java/org/springframework/data/convert/Jsr310Converters.java +++ b/src/main/java/org/springframework/data/convert/Jsr310Converters.java @@ -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.> asList(LocalDateTime.class, LocalDate.class, LocalTime.class, Instant.class) + .contains(type); + } + public static enum DateToLocalDateTimeConverter implements Converter { INSTANCE; diff --git a/src/main/java/org/springframework/data/convert/ThreeTenBackPortConverters.java b/src/main/java/org/springframework/data/convert/ThreeTenBackPortConverters.java index 5debec929..b56796037 100644 --- a/src/main/java/org/springframework/data/convert/ThreeTenBackPortConverters.java +++ b/src/main/java/org/springframework/data/convert/ThreeTenBackPortConverters.java @@ -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.> asList(LocalDateTime.class, LocalDate.class, LocalTime.class, Instant.class) + .contains(type); + } + public static enum DateToLocalDateTimeConverter implements Converter { INSTANCE; diff --git a/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java b/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java index 3453b31ed..658edceaf 100644 --- a/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java +++ b/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java @@ -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, ?> 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, ?> 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; } /* diff --git a/src/test/java/org/springframework/data/auditing/AuditingHandlerUnitTests.java b/src/test/java/org/springframework/data/auditing/AuditingHandlerUnitTests.java index 7c5571ff7..8b506aec6 100644 --- a/src/test/java/org/springframework/data/auditing/AuditingHandlerUnitTests.java +++ b/src/test/java/org/springframework/data/auditing/AuditingHandlerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2014 the original author or authors. + * Copyright 2008-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. @@ -18,10 +18,13 @@ package org.springframework.data.auditing; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import java.util.Collections; + import org.junit.Before; import org.junit.Test; import org.springframework.data.domain.AuditorAware; -import org.springframework.data.mapping.context.SampleMappingContext; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.context.PersistentEntities; /** * Unit test for {@code AuditingHandler}. @@ -48,7 +51,7 @@ public class AuditingHandlerUnitTests { } protected AuditingHandler getHandler() { - return new AuditingHandler(new SampleMappingContext()); + return new AuditingHandler(new PersistentEntities(Collections.> emptySet())); } /** diff --git a/src/test/java/org/springframework/data/auditing/AuditableBeanWrapperFactoryUnitTests.java b/src/test/java/org/springframework/data/auditing/DefaultAuditableBeanWrapperFactoryUnitTests.java similarity index 61% rename from src/test/java/org/springframework/data/auditing/AuditableBeanWrapperFactoryUnitTests.java rename to src/test/java/org/springframework/data/auditing/DefaultAuditableBeanWrapperFactoryUnitTests.java index 4160eff96..ec20e0db5 100644 --- a/src/test/java/org/springframework/data/auditing/AuditableBeanWrapperFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/auditing/DefaultAuditableBeanWrapperFactoryUnitTests.java @@ -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. @@ -18,17 +18,21 @@ package org.springframework.data.auditing; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import java.util.Calendar; + import org.junit.Test; -import org.springframework.data.auditing.AuditableBeanWrapperFactory.AuditableInterfaceBeanWrapper; -import org.springframework.data.auditing.AuditableBeanWrapperFactory.ReflectionAuditingBeanWrapper; +import org.springframework.data.auditing.DefaultAuditableBeanWrapperFactory.AuditableInterfaceBeanWrapper; +import org.springframework.data.auditing.DefaultAuditableBeanWrapperFactory.ReflectionAuditingBeanWrapper; /** + * Unit tests for {@link DefaultAuditableBeanWrapperFactory}. + * * @author Oliver Gierke * @since 1.5 */ -public class AuditableBeanWrapperFactoryUnitTests { +public class DefaultAuditableBeanWrapperFactoryUnitTests { - AuditableBeanWrapperFactory factory = new AuditableBeanWrapperFactory(); + DefaultAuditableBeanWrapperFactory factory = new DefaultAuditableBeanWrapperFactory(); @Test public void returnsNullForNullSource() { @@ -55,4 +59,21 @@ public class AuditableBeanWrapperFactoryUnitTests { AuditableBeanWrapper wrapper = factory.getBeanWrapperFor(new Object()); assertThat(wrapper, is(nullValue())); } + + /** + * @see DATACMNS-643 + */ + @Test + public void setsJsr310AndThreeTenBpTypes() { + + Jsr310ThreeTenBpAuditedUser user = new Jsr310ThreeTenBpAuditedUser(); + Calendar calendar = Calendar.getInstance(); + + AuditableBeanWrapper wrapper = factory.getBeanWrapperFor(user); + wrapper.setCreatedDate(calendar); + wrapper.setLastModifiedDate(calendar); + + assertThat(user.createdDate, is(notNullValue())); + assertThat(user.lastModifiedDate, is(notNullValue())); + } } diff --git a/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java b/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java index e0dfc9b8b..5e387aa9a 100644 --- a/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java +++ b/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2014 the original author or authors. + * Copyright 2008-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. @@ -19,14 +19,15 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import java.util.Collections; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.annotation.Id; -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.data.mapping.context.SampleMappingContext; /** @@ -47,7 +48,7 @@ public class IsNewAwareAuditingHandlerUnitTests extends AuditingHandlerUnitTests @Override protected IsNewAwareAuditingHandler getHandler() { - return new IsNewAwareAuditingHandler(mock(SampleMappingContext.class)); + return new IsNewAwareAuditingHandler(mock(PersistentEntities.class)); } @Test @@ -76,8 +77,7 @@ public class IsNewAwareAuditingHandlerUnitTests extends AuditingHandlerUnitTests */ @Test(expected = IllegalArgumentException.class) public void rejectsNullMappingContext() { - new IsNewAwareAuditingHandler( - (MappingContext, ? extends PersistentProperty>) null); + new IsNewAwareAuditingHandler((PersistentEntities) null); } /** @@ -85,7 +85,20 @@ public class IsNewAwareAuditingHandlerUnitTests extends AuditingHandlerUnitTests */ @Test public void setsUpHandlerWithMappingContext() { - new IsNewAwareAuditingHandler(new SampleMappingContext()); + new IsNewAwareAuditingHandler(new PersistentEntities(Collections.> emptySet())); + } + + /** + * @see DATACMNS-638 + */ + @Test + public void handlingNullIsANoOp() { + + IsNewAwareAuditingHandler handler = getHandler(); + + handler.markAudited(null); + handler.markCreated(null); + handler.markModified(null); } static class Domain { diff --git a/src/test/java/org/springframework/data/auditing/Jsr310ThreeTenBpAuditedUser.java b/src/test/java/org/springframework/data/auditing/Jsr310ThreeTenBpAuditedUser.java new file mode 100644 index 000000000..655fc519f --- /dev/null +++ b/src/test/java/org/springframework/data/auditing/Jsr310ThreeTenBpAuditedUser.java @@ -0,0 +1,30 @@ +/* + * 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. + * 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.time.LocalDateTime; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; + +/** + * @author Oliver Gierke + */ +public class Jsr310ThreeTenBpAuditedUser { + + @CreatedDate LocalDateTime createdDate; + @LastModifiedDate org.threeten.bp.LocalDateTime lastModifiedDate; +} diff --git a/src/test/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactoryUnitTests.java b/src/test/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactoryUnitTests.java index 34ef05606..85b0ec534 100644 --- a/src/test/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactoryUnitTests.java @@ -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. @@ -19,14 +19,22 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import java.time.LocalDateTime; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; import java.util.GregorianCalendar; +import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.LastModifiedBy; -import org.springframework.data.auditing.AuditableBeanWrapperFactory.AuditableInterfaceBeanWrapper; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.auditing.DefaultAuditableBeanWrapperFactory.AuditableInterfaceBeanWrapper; +import org.springframework.data.convert.Jsr310Converters.LocalDateTimeToDateConverter; import org.springframework.data.domain.Auditable; +import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.mapping.context.SampleMappingContext; /** @@ -37,11 +45,17 @@ import org.springframework.data.mapping.context.SampleMappingContext; */ public class MappingAuditableBeanWrapperFactoryUnitTests { - AuditableBeanWrapperFactory factory; + DefaultAuditableBeanWrapperFactory factory; @Before public void setUp() { - factory = new MappingAuditableBeanWrapperFactory(new SampleMappingContext()); + + SampleMappingContext context = new SampleMappingContext(); + context.setInitialEntitySet(Collections.singleton(Sample.class)); + context.afterPropertiesSet(); + + PersistentEntities entities = new PersistentEntities(Collections.singleton(context)); + factory = new MappingAuditableBeanWrapperFactory(entities); } /** @@ -104,10 +118,83 @@ public class MappingAuditableBeanWrapperFactoryUnitTests { is(instanceOf(AuditableInterfaceBeanWrapper.class))); } + /** + * @see DATACMNS-638 + */ + @Test + public void returnsLastModificationCalendarAsCalendar() { + + Date reference = new Date(); + + Calendar calendar = new GregorianCalendar(); + calendar.setTime(reference); + + assertLastModificationDate(calendar, reference); + } + + /** + * @see DATACMNS-638 + */ + @Test + public void returnsLastModificationDateTimeAsCalendar() { + + DateTime reference = new DateTime(); + + assertLastModificationDate(reference, reference.toDate()); + } + + /** + * @see DATACMNS-638 + */ + @Test + public void returnsLastModificationDateAsCalendar() { + + Date reference = new Date(); + + assertLastModificationDate(reference, reference); + } + + /** + * @see DATACMNS-638, DATACMNS-43 + */ + @Test + public void returnsLastModificationJsr310DateTimeAsCalendar() { + + LocalDateTime reference = LocalDateTime.now(); + + assertLastModificationDate(reference, LocalDateTimeToDateConverter.INSTANCE.convert(reference)); + } + + /** + * @see DATACMNS-638, DATACMNS-43 + */ + @Test + public void returnsLastModificationThreeTenBpDateTimeAsCalendar() { + + org.threeten.bp.LocalDateTime reference = org.threeten.bp.LocalDateTime.now(); + + assertLastModificationDate(reference, + org.springframework.data.convert.ThreeTenBackPortConverters.LocalDateTimeToDateConverter.INSTANCE + .convert(reference)); + } + + private final void assertLastModificationDate(Object source, Date expected) { + + Calendar calendar = new GregorianCalendar(); + calendar.setTime(expected); + + Sample sample = new Sample(); + sample.lastModifiedDate = source; + + AuditableBeanWrapper wrapper = factory.getBeanWrapperFor(sample); + assertThat(wrapper.getLastModifiedDate(), is(calendar)); + } + static class Sample { - @CreatedBy private Object createdBy; + private @CreatedBy Object createdBy; private Object lastModifiedBy; + private @LastModifiedDate Object lastModifiedDate; @LastModifiedBy public Object getLastModifiedBy() { diff --git a/src/test/java/org/springframework/data/auditing/ReflectionAuditingBeanWrapperUnitTests.java b/src/test/java/org/springframework/data/auditing/ReflectionAuditingBeanWrapperUnitTests.java index d2521c8ce..c635462f5 100644 --- a/src/test/java/org/springframework/data/auditing/ReflectionAuditingBeanWrapperUnitTests.java +++ b/src/test/java/org/springframework/data/auditing/ReflectionAuditingBeanWrapperUnitTests.java @@ -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. @@ -26,7 +26,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.auditing.AuditableBeanWrapperFactory.ReflectionAuditingBeanWrapper; +import org.springframework.data.auditing.DefaultAuditableBeanWrapperFactory.ReflectionAuditingBeanWrapper; /** * Unit tests for {@link ReflectionAuditingBeanWrapper}. diff --git a/src/test/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactoryUnitTests.java b/src/test/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactoryUnitTests.java index 15b696a6d..f3872d665 100644 --- a/src/test/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 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. @@ -18,18 +18,23 @@ package org.springframework.data.mapping.context; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; + import org.junit.Before; import org.junit.Test; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Version; import org.springframework.data.domain.Persistable; -import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory; import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory.PropertyIsNullIsNewStrategy; import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory.PropertyIsNullOrZeroNumberIsNewStrategy; import org.springframework.data.support.IsNewStrategy; import org.springframework.data.support.IsNewStrategyFactory; /** + * Unit tests for {@link MappingContextIsNewStrategyFactory}. + * * @author Oliver Gierke */ public class MappingContextIsNewStrategyFactoryUnitTests { @@ -40,7 +45,10 @@ public class MappingContextIsNewStrategyFactoryUnitTests { public void setUp() { SampleMappingContext context = new SampleMappingContext(); - factory = new MappingContextIsNewStrategyFactory(context); + context.setInitialEntitySet(new HashSet>(Arrays.> asList(Entity.class, VersionedEntity.class))); + context.afterPropertiesSet(); + + factory = new MappingContextIsNewStrategyFactory(new PersistentEntities(Collections.singleton(context))); } @Test @@ -94,11 +102,9 @@ public class MappingContextIsNewStrategyFactoryUnitTests { @SuppressWarnings("serial") static class PersistableEntity implements Persistable { - @Version - Long version; + @Version Long version; - @Id - Long id; + @Id Long id; boolean isNew = true; @@ -113,25 +119,20 @@ public class MappingContextIsNewStrategyFactoryUnitTests { static class VersionedEntity { - @Version - Long version; + @Version Long version; - @Id - Long id; + @Id Long id; } static class PrimitveVersionedEntity { - @Version - long version = 0; + @Version long version = 0; - @Id - Long id; + @Id Long id; } static class Entity { - @Id - Long id; + @Id Long id; } }