Skip to content

Commit d4e9410

Browse files
DATACMNS-1231 - Introduce dedicated Auditor value object.
Original Pull Request: #458
1 parent d3af4a7 commit d4e9410

File tree

8 files changed

+245
-50
lines changed

8 files changed

+245
-50
lines changed

src/main/java/org/springframework/data/auditing/AuditingHandler.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
import org.apache.commons.logging.Log;
2121
import org.apache.commons.logging.LogFactory;
22-
2322
import org.springframework.beans.factory.InitializingBean;
2423
import org.springframework.data.domain.AuditorAware;
2524
import org.springframework.data.mapping.PersistentEntity;
@@ -90,7 +89,7 @@ public <T> T markCreated(T source) {
9089

9190
Assert.notNull(source, "Entity must not be null!");
9291

93-
return markCreated(auditorAware.flatMap(AuditorAware::getCurrentAuditor).orElse(null), source);
92+
return markCreated(getAuditor(), source);
9493
}
9594

9695
/**
@@ -102,7 +101,13 @@ public <T> T markModified(T source) {
102101

103102
Assert.notNull(source, "Entity must not be null!");
104103

105-
return markModified(auditorAware.flatMap(AuditorAware::getCurrentAuditor).orElse(null), source);
104+
return markModified(getAuditor(), source);
105+
}
106+
107+
Auditor<?> getAuditor() {
108+
109+
return auditorAware.map(AuditorAware::getCurrentAuditor).map(Auditor::ofOptional) //
110+
.orElse(Auditor.none());
106111
}
107112

108113
/*
@@ -111,8 +116,6 @@ public <T> T markModified(T source) {
111116
*/
112117
public void afterPropertiesSet() {
113118

114-
super.afterPropertiesSet();
115-
116119
if (!auditorAware.isPresent()) {
117120
logger.debug("No AuditorAware set! Auditing will not be applied!");
118121
}

src/main/java/org/springframework/data/auditing/AuditingHandlerSupport.java

+22-28
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.apache.commons.logging.Log;
2222
import org.apache.commons.logging.LogFactory;
2323

24-
import org.springframework.beans.factory.InitializingBean;
2524
import org.springframework.core.log.LogMessage;
2625
import org.springframework.data.domain.Auditable;
2726
import org.springframework.data.mapping.context.PersistentEntities;
@@ -36,11 +35,11 @@
3635
* @author Mark Paluch
3736
* @since 2.4
3837
*/
39-
public abstract class AuditingHandlerSupport implements InitializingBean {
38+
public abstract class AuditingHandlerSupport {
4039

4140
private static final Log logger = LogFactory.getLog(AuditingHandlerSupport.class);
4241

43-
private final DefaultAuditableBeanWrapperFactory factory;
42+
private final AuditableBeanWrapperFactory factory;
4443

4544
private DateTimeProvider dateTimeProvider = CurrentDateTimeProvider.INSTANCE;
4645
private boolean dateTimeForNow = true;
@@ -83,40 +82,34 @@ public void setModifyOnCreation(boolean modifyOnCreation) {
8382
/**
8483
* Sets the {@link DateTimeProvider} to be used to determine the dates to be set.
8584
*
86-
* @param dateTimeProvider
85+
* @param dateTimeProvider can be {@literal null}, defaults to {@link CurrentDateTimeProvider} in that case.
8786
*/
8887
public void setDateTimeProvider(@Nullable DateTimeProvider dateTimeProvider) {
8988
this.dateTimeProvider = dateTimeProvider == null ? CurrentDateTimeProvider.INSTANCE : dateTimeProvider;
9089
}
9190

92-
/*
93-
* (non-Javadoc)
94-
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
95-
*/
96-
public void afterPropertiesSet() {}
97-
9891
/**
99-
* Returns whether the given source is considered to be auditable in the first place
92+
* Returns whether the given source is considered to be auditable in the first place.
10093
*
10194
* @param source must not be {@literal null}.
102-
* @return
95+
* @return {@literal true} if the given {@literal source} considered to be auditable.
10396
*/
10497
protected final boolean isAuditable(Object source) {
10598

106-
Assert.notNull(source, "Source must not be null!");
99+
Assert.notNull(source, "Source entity must not be null!");
107100

108101
return factory.getBeanWrapperFor(source).isPresent();
109102
}
110103

111104
/**
112105
* Marks the given object as created.
113106
*
114-
* @param auditor
115-
* @param source
107+
* @param auditor can be {@literal null}.
108+
* @param source must not be {@literal null}.
116109
*/
117-
<T> T markCreated(@Nullable Object auditor, T source) {
110+
<T> T markCreated(Auditor auditor, T source) {
118111

119-
Assert.notNull(source, "Entity must not be null!");
112+
Assert.notNull(source, "Source entity must not be null!");
120113

121114
return touch(auditor, source, true);
122115
}
@@ -127,28 +120,26 @@ <T> T markCreated(@Nullable Object auditor, T source) {
127120
* @param auditor
128121
* @param source
129122
*/
130-
<T> T markModified(@Nullable Object auditor, T source) {
123+
<T> T markModified(Auditor auditor, T source) {
131124

132-
Assert.notNull(source, "Entity must not be null!");
125+
Assert.notNull(source, "Source entity must not be null!");
133126

134127
return touch(auditor, source, false);
135128
}
136129

137-
private <T> T touch(@Nullable Object auditor, T target, boolean isNew) {
130+
private <T> T touch(Auditor auditor, T target, boolean isNew) {
138131

139132
Optional<AuditableBeanWrapper<T>> wrapper = factory.getBeanWrapperFor(target);
140133

141134
return wrapper.map(it -> {
142135

143-
if (auditor != null) {
144-
touchAuditor(auditor, it, isNew);
145-
}
136+
touchAuditor(auditor, it, isNew);
146137
Optional<TemporalAccessor> now = dateTimeForNow ? touchDate(it, isNew) : Optional.empty();
147138

148139
if (logger.isDebugEnabled()) {
149140

150141
Object defaultedNow = now.map(Object::toString).orElse("not set");
151-
Object defaultedAuditor = auditor != null ? auditor.toString() : "unknown";
142+
Object defaultedAuditor = auditor.isPresent() ? auditor.toString() : "unknown";
152143

153144
logger.debug(
154145
LogMessage.format("Touched %s - Last modification at %s by %s", target, defaultedNow, defaultedAuditor));
@@ -166,17 +157,20 @@ private <T> T touch(@Nullable Object auditor, T target, boolean isNew) {
166157
* @param isNew
167158
* @return
168159
*/
169-
private void touchAuditor(Object auditor, AuditableBeanWrapper<?> wrapper, boolean isNew) {
160+
private void touchAuditor(Auditor auditor, AuditableBeanWrapper<?> wrapper, boolean isNew) {
161+
162+
if(!auditor.isPresent()) {
163+
return;
164+
}
170165

171166
Assert.notNull(wrapper, "AuditableBeanWrapper must not be null!");
172-
Assert.notNull(auditor, "Auditor must not be null!");
173167

174168
if (isNew) {
175-
wrapper.setCreatedBy(auditor);
169+
wrapper.setCreatedBy(auditor.getValue());
176170
}
177171

178172
if (!isNew || modifyOnCreation) {
179-
wrapper.setLastModifiedBy(auditor);
173+
wrapper.setLastModifiedBy(auditor.getValue());
180174
}
181175
}
182176

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright 2020 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+
* https://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.auditing;
17+
18+
import java.util.Optional;
19+
20+
import org.springframework.lang.Nullable;
21+
import org.springframework.util.ObjectUtils;
22+
23+
/**
24+
* Value Object encapsulating the actual auditor value.
25+
*
26+
* @author Christoph Strobl
27+
* @since 2.4
28+
*/
29+
class Auditor<T> {
30+
31+
private static final Auditor NONE = new Auditor(null) {
32+
33+
@Override
34+
public boolean isPresent() {
35+
return false;
36+
}
37+
};
38+
39+
private final @Nullable T value;
40+
41+
private Auditor(@Nullable T value) {
42+
this.value = value;
43+
}
44+
45+
/**
46+
* @return
47+
*/
48+
@Nullable
49+
public T getValue() {
50+
return value;
51+
}
52+
53+
/**
54+
* Create an {@link Auditor} for the given {@literal source} value. <br />
55+
* If the given {@literal source} is {@literal null} {@link Auditor#none()} is returned. A source that already is an
56+
* {@link Auditor} gets returned as is.
57+
*
58+
* @param source can be {@literal null}.
59+
* @param <T>
60+
* @return {@link Auditor#none()} if the given {@literal source} is {@literal null}. }
61+
*/
62+
public static <T> Auditor<T> of(@Nullable T source) {
63+
64+
if (source instanceof Auditor) {
65+
return (Auditor) source;
66+
}
67+
68+
return source == null ? Auditor.none() : new Auditor<>(source);
69+
}
70+
71+
/**
72+
* Create an {@link Auditor} for the given {@link Optional} value. <br />
73+
* If the given {@literal source} is {@link Optional#empty()} {@link Auditor#none()} is returned. An {@link Optional}
74+
* wrapping and {@link Auditor} returns the unwrapped {@link Auditor} instance as is.
75+
*
76+
* @param source must not be {@literal null}.
77+
* @param <T>
78+
* @return {@link Auditor#none()} if the given {@literal source} is {@literal null}. }
79+
*/
80+
public static <T> Auditor<T> ofOptional(@Nullable Optional<T> source) {
81+
return Auditor.of(source.orElse(null));
82+
}
83+
84+
/**
85+
* Return an {@link Auditor} that is not present.
86+
*
87+
* @param <T>
88+
* @return never {@literal null}.
89+
*/
90+
public static <T> Auditor<T> none() {
91+
return NONE;
92+
}
93+
94+
/**
95+
* @return {@literal true} if {@link #getValue()} returns a non {@literal null} value.
96+
*/
97+
public boolean isPresent() {
98+
return getValue() != null;
99+
}
100+
101+
@Override
102+
public String toString() {
103+
return value != null ? value.toString() : "Auditor.none()";
104+
}
105+
106+
@Override
107+
public boolean equals(Object o) {
108+
if (this == o)
109+
return true;
110+
if (o == null || getClass() != o.getClass())
111+
return false;
112+
113+
Auditor<?> auditor = (Auditor<?>) o;
114+
115+
return ObjectUtils.nullSafeEquals(value, auditor.value);
116+
}
117+
118+
@Override
119+
public int hashCode() {
120+
return ObjectUtils.nullSafeHashCode(value);
121+
}
122+
}

src/main/java/org/springframework/data/auditing/ReactiveAuditingHandler.java

+14-10
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
import reactor.core.publisher.Mono;
1919

20-
import java.util.Optional;
21-
2220
import org.springframework.data.domain.ReactiveAuditorAware;
2321
import org.springframework.data.mapping.context.PersistentEntities;
2422
import org.springframework.util.Assert;
@@ -27,15 +25,16 @@
2725
* Auditing handler to mark entity objects created and modified.
2826
*
2927
* @author Mark Paluch
28+
* @author Christoph Strobl
3029
* @since 2.4
3130
*/
3231
public class ReactiveAuditingHandler extends AuditingHandlerSupport {
3332

3433
private ReactiveAuditorAware<?> auditorAware = Mono::empty;
3534

3635
/**
37-
* Creates a new {@link AuditableBeanWrapper} using the given {@link PersistentEntities} when looking up auditing
38-
* metadata via reflection.
36+
* Creates a new {@link ReactiveAuditingHandler} using the given {@link PersistentEntities} when looking up auditing
37+
* metadata.
3938
*
4039
* @param entities must not be {@literal null}.
4140
*/
@@ -63,9 +62,8 @@ public <T> Mono<T> markCreated(T source) {
6362

6463
Assert.notNull(source, "Entity must not be null!");
6564

66-
return auditorAware.getCurrentAuditor().map(Optional::of) //
67-
.defaultIfEmpty(Optional.empty()) //
68-
.map(auditor -> markCreated(auditor.orElse(null), source));
65+
return getAuditor() //
66+
.map(auditor -> markCreated(auditor, source));
6967
}
7068

7169
/**
@@ -77,8 +75,14 @@ public <T> Mono<T> markModified(T source) {
7775

7876
Assert.notNull(source, "Entity must not be null!");
7977

80-
return auditorAware.getCurrentAuditor().map(Optional::of) //
81-
.defaultIfEmpty(Optional.empty()) //
82-
.map(auditor -> markModified(auditor.orElse(null), source));
78+
return getAuditor() //
79+
.map(auditor -> markModified(auditor, source));
80+
}
81+
82+
private Mono<? extends Auditor<?>> getAuditor() {
83+
84+
return auditorAware.getCurrentAuditor() //
85+
.map(Auditor::of) //
86+
.defaultIfEmpty(Auditor.none());
8387
}
8488
}

src/main/java/org/springframework/data/domain/ReactiveAuditorAware.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2008-2020 the original author or authors.
2+
* Copyright 2020 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.
@@ -27,9 +27,10 @@
2727
public interface ReactiveAuditorAware<T> {
2828

2929
/**
30-
* Returns the current auditor of the application.
30+
* Returns a {@link Mono} publishing the current auditor of the application.
3131
*
32-
* @return the current auditor. If the mono does not emit a value, the auditor is considered to be unknown.
32+
* @return the {@link Mono} emitting the current auditor, or an empty one, if the auditor is considered to be unknown.
3333
*/
3434
Mono<T> getCurrentAuditor();
35+
3536
}

0 commit comments

Comments
 (0)