Skip to content

Commit 9ae8caa

Browse files
committed
DATAJPA-9 - Added DateTimeProvider SPI for auditing.
Extracted the DateTime instance calculation to be used for auditing into a DateTimeProvider callback interface to open it up for customization. AuditingEntityListener can get an instance of it configured. Exposed the property via the auditing namespace. By default a CurrentDateTimeProvider its used that contains the behavior we had so far.
1 parent bb60623 commit 9ae8caa

File tree

9 files changed

+232
-17
lines changed

9 files changed

+232
-17
lines changed

src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2008-2011 the original author or authors.
2+
* Copyright 2008-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -55,6 +55,7 @@ public class AuditingEntityListener<T> implements InitializingBean {
5555
private static final Logger LOG = LoggerFactory.getLogger(AuditingEntityListener.class);
5656

5757
private AuditorAware<T> auditorAware;
58+
private DateTimeProvider dateTimeProvider = CurrentDateTimeProvider.INSTANCE;
5859

5960
private boolean dateTimeForNow = true;
6061
private boolean modifyOnCreation = true;
@@ -78,7 +79,6 @@ public void setAuditorAware(final AuditorAware<T> auditorAware) {
7879
* @param dateTimeForNow the dateTimeForNow to set
7980
*/
8081
public void setDateTimeForNow(boolean dateTimeForNow) {
81-
8282
this.dateTimeForNow = dateTimeForNow;
8383
}
8484

@@ -89,10 +89,18 @@ public void setDateTimeForNow(boolean dateTimeForNow) {
8989
* @param modifyOnCreation if modification information shall be set on creation, too
9090
*/
9191
public void setModifyOnCreation(final boolean modifyOnCreation) {
92-
9392
this.modifyOnCreation = modifyOnCreation;
9493
}
9594

95+
/**
96+
* Sets the {@link DateTimeProvider} to be used to determine the dates to be set.
97+
*
98+
* @param dateTimeProvider
99+
*/
100+
public void setDateTimeProvider(DateTimeProvider dateTimeProvider) {
101+
this.dateTimeProvider = dateTimeProvider == null ? CurrentDateTimeProvider.INSTANCE : dateTimeProvider;
102+
}
103+
96104
/**
97105
* Sets modification and creation date and auditor on the target object in case it implements {@link Auditable} on
98106
* persist events.
@@ -171,7 +179,7 @@ private T touchAuditor(final Auditable<T, ?> auditable, boolean isNew) {
171179
*/
172180
private DateTime touchDate(final Auditable<T, ?> auditable, boolean isNew) {
173181

174-
DateTime now = new DateTime();
182+
DateTime now = dateTimeProvider.getDateTime();
175183

176184
if (isNew) {
177185
auditable.setCreatedDate(now);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.domain.support;
17+
18+
import org.joda.time.DateTime;
19+
20+
/**
21+
* Default {@link DateTimeProvider} simply creating new {@link DateTime} instances for each method call.
22+
*
23+
* @author Oliver Gierke
24+
*/
25+
public enum CurrentDateTimeProvider implements DateTimeProvider {
26+
27+
INSTANCE;
28+
29+
/*
30+
* (non-Javadoc)
31+
* @see org.springframework.data.jpa.domain.support.DateTimeProvider#getDateTime()
32+
*/
33+
public DateTime getDateTime() {
34+
return new DateTime();
35+
}
36+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.domain.support;
17+
18+
import org.joda.time.DateTime;
19+
20+
/**
21+
* SPI to calculate the {@link DateTime} instance to be used when auditing.
22+
*
23+
* @author Oliver Gierke
24+
*/
25+
public interface DateTimeProvider {
26+
27+
/**
28+
* Returns the {@link DateTime} to be used as modification date.
29+
*
30+
* @return
31+
*/
32+
DateTime getDateTime();
33+
}

src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2008-2011 the original author or authors.
2+
* Copyright 2008-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -61,6 +61,11 @@ public BeanDefinition parse(Element element, ParserContext parser) {
6161

6262
builder.addPropertyValue("dateTimeForNow", element.getAttribute("set-dates"));
6363

64+
String dateTimeProviderRef = element.getAttribute("date-time-provider-ref");
65+
if (StringUtils.hasText(dateTimeProviderRef)) {
66+
builder.addPropertyReference("dateTimeProvider", dateTimeProviderRef);
67+
}
68+
6469
registerInfrastructureBeanWithId(builder.getRawBeanDefinition(), AUDITING_ENTITY_LISTENER_CLASS_NAME, parser,
6570
element);
6671

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
http\://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd=org/springframework/data/jpa/repository/config/spring-jpa-1.0.xsd
2-
http\://www.springframework.org/schema/data/jpa/spring-jpa.xsd=org/springframework/data/jpa/repository/config/spring-jpa-1.0.xsd
2+
http\://www.springframework.org/schema/data/jpa/spring-jpa-1.1.xsd=org/springframework/data/jpa/repository/config/spring-jpa-1.1.xsd
3+
http\://www.springframework.org/schema/data/jpa/spring-jpa.xsd=org/springframework/data/jpa/repository/config/spring-jpa-1.1.xsd
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<xsd:schema xmlns="http://www.springframework.org/schema/data/jpa"
3+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
4+
xmlns:tool="http://www.springframework.org/schema/tool"
5+
xmlns:context="http://www.springframework.org/schema/context"
6+
xmlns:repository="http://www.springframework.org/schema/data/repository"
7+
targetNamespace="http://www.springframework.org/schema/data/jpa"
8+
elementFormDefault="qualified" attributeFormDefault="unqualified">
9+
10+
<xsd:import namespace="http://www.springframework.org/schema/tool" />
11+
<xsd:import namespace="http://www.springframework.org/schema/context"
12+
schemaLocation="http://www.springframework.org/schema/context/spring-context.xsd" />
13+
<xsd:import namespace="http://www.springframework.org/schema/data/repository"
14+
schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository.xsd" />
15+
16+
<xsd:complexType name="jpa-repository">
17+
<xsd:complexContent>
18+
<xsd:extension base="repository:repository">
19+
<xsd:attributeGroup ref="repository:transactional-repository-attributes" />
20+
<xsd:attribute name="entity-manager-factory-ref" type="entityManagerFactoryRef" />
21+
</xsd:extension>
22+
</xsd:complexContent>
23+
</xsd:complexType>
24+
25+
<xsd:element name="repositories">
26+
<xsd:complexType>
27+
<xsd:complexContent>
28+
<xsd:extension base="repository:repositories">
29+
<xsd:sequence>
30+
<xsd:element name="repository" minOccurs="0" maxOccurs="unbounded" type="jpa-repository" />
31+
</xsd:sequence>
32+
<xsd:attributeGroup ref="repository:transactional-repository-attributes" />
33+
<xsd:attribute name="entity-manager-factory-ref" type="entityManagerFactoryRef" />
34+
</xsd:extension>
35+
</xsd:complexContent>
36+
</xsd:complexType>
37+
</xsd:element>
38+
39+
<xsd:element name="auditing">
40+
<xsd:complexType>
41+
<xsd:attribute name="auditor-aware-ref">
42+
<xsd:annotation>
43+
<xsd:appinfo>
44+
<tool:annotation kind="ref">
45+
<tool:assignable-to type="org.springframework.data.domain.AuditorAware" />
46+
</tool:annotation>
47+
</xsd:appinfo>
48+
</xsd:annotation>
49+
</xsd:attribute>
50+
<xsd:attribute name="set-dates" default="true" type="xsd:boolean" />
51+
<xsd:attribute name="date-time-provider-ref">
52+
<xsd:annotation>
53+
<xsd:appinfo>
54+
<tool:annotation kind="ref">
55+
<tool:assignable-to type="org.springframework.data.jpa.domain.support.DateTimeProvider" />
56+
</tool:annotation>
57+
</xsd:appinfo>
58+
</xsd:annotation>
59+
</xsd:attribute>
60+
</xsd:complexType>
61+
</xsd:element>
62+
63+
<xsd:simpleType name="entityManagerFactoryRef">
64+
<xsd:annotation>
65+
<xsd:appinfo>
66+
<tool:annotation kind="ref">
67+
<tool:assignable-to type="org.springframework.orm.jpa.AbstractEntityManagerFactoryBean" />
68+
</tool:annotation>
69+
</xsd:appinfo>
70+
</xsd:annotation>
71+
<xsd:union memberTypes="xsd:string" />
72+
</xsd:simpleType>
73+
74+
75+
</xsd:schema>

src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerUnitTests.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2008-2011 the original author or authors.
2+
* Copyright 2008-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -124,7 +124,7 @@ public void onlySetsModificationDataOnNotNewEntities() {
124124
}
125125

126126
@Test
127-
public void doesNotSetTimeIfConfigured() throws Exception {
127+
public void doesNotSetTimeIfConfigured() {
128128

129129
listener.setDateTimeForNow(false);
130130
listener.setAuditorAware(auditorAware);
@@ -136,4 +136,18 @@ public void doesNotSetTimeIfConfigured() throws Exception {
136136
assertNotNull(user.getLastModifiedBy());
137137
assertNull(user.getLastModifiedDate());
138138
}
139+
140+
/**
141+
* @see DATAJPA-9
142+
*/
143+
@Test
144+
public void usesDateTimeProviderIfConfigured() {
145+
146+
DateTimeProvider provider = mock(DateTimeProvider.class);
147+
148+
listener.setDateTimeProvider(provider);
149+
listener.touchForCreate(user);
150+
151+
verify(provider, times(1)).getDateTime();
152+
}
139153
}

src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2008-2011 the original author or authors.
2+
* Copyright 2008-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,7 +20,9 @@
2020

2121
import org.junit.Test;
2222
import org.springframework.beans.PropertyValue;
23+
import org.springframework.beans.factory.BeanFactory;
2324
import org.springframework.beans.factory.config.BeanDefinition;
25+
import org.springframework.beans.factory.config.RuntimeBeanReference;
2426
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
2527
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
2628
import org.springframework.core.io.ClassPathResource;
@@ -33,27 +35,54 @@
3335
public class AuditingBeanDefinitionParserTests {
3436

3537
@Test
36-
public void settingDatesIsConfigured() throws Exception {
38+
public void settingDatesIsConfigured() {
3739

3840
assertSetDatesIsSetTo("auditing/auditing-namespace-context.xml", "true");
3941
}
4042

4143
@Test
42-
public void notSettingDatesIsConfigured() throws Exception {
44+
public void notSettingDatesIsConfigured() {
4345

4446
assertSetDatesIsSetTo("auditing/auditing-namespace-context2.xml", "false");
4547
}
4648

47-
private void assertSetDatesIsSetTo(String configFile, String value) {
49+
/**
50+
* @see DATAJPA-9
51+
*/
52+
@Test
53+
public void wiresDateTimeProviderIfConfigured() {
4854

49-
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
50-
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
51-
reader.loadBeanDefinitions(new ClassPathResource(configFile));
55+
String location = "auditing/auditing-namespace-context3.xml";
56+
BeanDefinition definition = getBeanDefinition(location);
57+
PropertyValue value = definition.getPropertyValues().getPropertyValue("dateTimeProvider");
58+
59+
assertThat(value, is(notNullValue()));
60+
assertThat(value.getValue(), is(RuntimeBeanReference.class));
61+
assertThat(((RuntimeBeanReference) value.getValue()).getBeanName(), is("dateTimeProvider"));
62+
63+
BeanFactory factory = loadFactoryFrom(location);
64+
Object bean = factory.getBean(AuditingBeanDefinitionParser.AUDITING_ENTITY_LISTENER_CLASS_NAME);
65+
assertThat(bean, is(notNullValue()));
66+
}
67+
68+
private void assertSetDatesIsSetTo(String configFile, String value) {
5269

53-
BeanDefinition definition = factory
54-
.getBeanDefinition(AuditingBeanDefinitionParser.AUDITING_ENTITY_LISTENER_CLASS_NAME);
70+
BeanDefinition definition = getBeanDefinition(configFile);
5571
PropertyValue propertyValue = definition.getPropertyValues().getPropertyValue("dateTimeForNow");
5672
assertThat(propertyValue, is(notNullValue()));
5773
assertThat((String) propertyValue.getValue(), is(value));
5874
}
75+
76+
private BeanDefinition getBeanDefinition(String configFile) {
77+
78+
DefaultListableBeanFactory factory = loadFactoryFrom(configFile);
79+
return factory.getBeanDefinition(AuditingBeanDefinitionParser.AUDITING_ENTITY_LISTENER_CLASS_NAME);
80+
}
81+
82+
private DefaultListableBeanFactory loadFactoryFrom(String configFile) {
83+
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
84+
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);
85+
xmlBeanDefinitionReader.loadBeanDefinitions(new ClassPathResource(configFile));
86+
return factory;
87+
}
5988
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<beans xmlns="http://www.springframework.org/schema/beans"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
5+
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
6+
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
7+
8+
<jpa:auditing set-dates="false" date-time-provider-ref="dateTimeProvider" />
9+
10+
<bean id="dateTimeProvider" class="org.mockito.Mockito" factory-method="mock">
11+
<constructor-arg value="org.springframework.data.jpa.domain.support.DateTimeProvider" />
12+
</bean>
13+
14+
</beans>

0 commit comments

Comments
 (0)