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:
Oliver Gierke
2015-02-04 19:30:02 +01:00
parent fe49e4cf14
commit 0c65c7bb67
17 changed files with 656 additions and 313 deletions

View File

@@ -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.<MappingContext<?, ?>> emptySet()));
}
/**

View File

@@ -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()));
}
}

View File

@@ -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 PersistentEntity<?, ?>, ? 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.<MappingContext<?, ?>> emptySet()));
}
/**
* @see DATACMNS-638
*/
@Test
public void handlingNullIsANoOp() {
IsNewAwareAuditingHandler handler = getHandler();
handler.markAudited(null);
handler.markCreated(null);
handler.markModified(null);
}
static class Domain {

View File

@@ -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;
}

View File

@@ -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() {

View File

@@ -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}.

View File

@@ -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<Class<?>>(Arrays.<Class<?>> 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<Long> {
@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;
}
}