Skip to content

Commit 62eed6c

Browse files
DATAGRAPH-1403 - Add reactive DataNeo4jAuditing support.
This change makes use of the new ReactiveAuditorAware and ReactiveIsNewAwareAuditingHandler infrastructure for fully reactive auditing support. The support is activated with the new `@ EnableReactiveNeo4jAuditing` annotation.
1 parent f2b977c commit 62eed6c

File tree

6 files changed

+183
-53
lines changed

6 files changed

+183
-53
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2011-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.neo4j.config;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Inherited;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import org.springframework.context.annotation.Import;
26+
import org.springframework.data.auditing.DateTimeProvider;
27+
import org.springframework.data.domain.AuditorAware;
28+
29+
/**
30+
* Annotation to enable auditing for SDN entities using reactive infrastructure via annotation configuration.
31+
*
32+
* @author Michael J. Simons
33+
* @since 6.0
34+
* @soundtrack Ferris MC - Missglückte Asimetrie
35+
*/
36+
@Inherited
37+
@Documented
38+
@Target(ElementType.TYPE)
39+
@Retention(RetentionPolicy.RUNTIME)
40+
@Import(ReactiveNeo4jAuditingRegistrar.class)
41+
public @interface EnableReactiveNeo4jAuditing {
42+
43+
/**
44+
* Configures the {@link AuditorAware} bean to be used to lookup the current principal.
45+
*
46+
* @return
47+
*/
48+
String auditorAwareRef() default "";
49+
50+
/**
51+
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
52+
*
53+
* @return
54+
*/
55+
boolean setDates() default true;
56+
57+
/**
58+
* Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}.
59+
*
60+
* @return
61+
*/
62+
boolean modifyOnCreate() default true;
63+
64+
/**
65+
* Configures a {@link DateTimeProvider} bean name that allows customizing actual date time class to be used for
66+
* setting creation and modification dates.
67+
*
68+
* @return
69+
*/
70+
String dateTimeProviderRef() default "";
71+
}

src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingRegistrar.java

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,13 @@
2020
import org.springframework.beans.factory.config.BeanDefinition;
2121
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
2222
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
23-
import org.springframework.core.type.AnnotationMetadata;
2423
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
2524
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
2625
import org.springframework.data.auditing.config.AuditingConfiguration;
2726
import org.springframework.data.config.ParsingUtils;
2827
import org.springframework.data.mapping.context.PersistentEntities;
2928
import org.springframework.data.neo4j.repository.event.AuditingBeforeBindCallback;
30-
import org.springframework.data.neo4j.repository.event.ReactiveAuditingBeforeBindCallback;
3129
import org.springframework.util.Assert;
32-
import org.springframework.util.ClassUtils;
3330

3431
/**
3532
* @author Michael J. Simons
@@ -38,9 +35,6 @@
3835
*/
3936
final class Neo4jAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
4037

41-
private static final boolean PROJECT_REACTOR_AVAILABLE = ClassUtils.isPresent("reactor.core.publisher.Mono",
42-
Neo4jAuditingRegistrar.class.getClassLoader());
43-
4438
private static final String AUDITING_HANDLER_BEAN_NAME = "neo4jAuditingHandler";
4539
private static final String MAPPING_CONTEXT_BEAN_NAME = "neo4jMappingContext";
4640

@@ -62,19 +56,6 @@ protected String getAuditingHandlerBeanName() {
6256
return AUDITING_HANDLER_BEAN_NAME;
6357
}
6458

65-
/*
66-
* (non-Javadoc)
67-
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
68-
*/
69-
@Override
70-
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
71-
72-
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
73-
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
74-
75-
super.registerBeanDefinitions(annotationMetadata, registry);
76-
}
77-
7859
/*
7960
* (non-Javadoc)
8061
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)
@@ -88,15 +69,10 @@ protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandle
8869

8970
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
9071
.rootBeanDefinition(AuditingBeforeBindCallback.class);
91-
listenerBeanDefinitionBuilder
92-
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
72+
listenerBeanDefinitionBuilder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
9373

9474
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
9575
AuditingBeforeBindCallback.class.getName(), registry);
96-
97-
if (PROJECT_REACTOR_AVAILABLE) {
98-
registerReactiveAuditingEntityCallback(registry, auditingHandlerDefinition.getSource());
99-
}
10076
}
10177

10278
/*
@@ -117,15 +93,4 @@ protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingCon
11793
builder.addConstructorArgValue(persistentEntities.getBeanDefinition());
11894
return configureDefaultAuditHandlerAttributes(configuration, builder);
11995
}
120-
121-
private void registerReactiveAuditingEntityCallback(BeanDefinitionRegistry registry, Object source) {
122-
123-
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingBeforeBindCallback.class);
124-
125-
builder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
126-
builder.getRawBeanDefinition().setSource(source);
127-
128-
registerInfrastructureBeanWithId(builder.getBeanDefinition(), ReactiveAuditingBeforeBindCallback.class.getName(),
129-
registry);
130-
}
13196
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2011-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.neo4j.config;
17+
18+
import java.lang.annotation.Annotation;
19+
20+
import org.springframework.beans.factory.config.BeanDefinition;
21+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
22+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
23+
import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler;
24+
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
25+
import org.springframework.data.auditing.config.AuditingConfiguration;
26+
import org.springframework.data.config.ParsingUtils;
27+
import org.springframework.data.mapping.context.PersistentEntities;
28+
import org.springframework.data.neo4j.repository.event.ReactiveAuditingBeforeBindCallback;
29+
import org.springframework.util.Assert;
30+
31+
/**
32+
* @author Michael J. Simons
33+
* @soundtrack Ferris MC - Missglückte Asimetrie
34+
* @since 6.0
35+
*/
36+
final class ReactiveNeo4jAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
37+
38+
private static final String AUDITING_HANDLER_BEAN_NAME = "reactiveNeo4jAuditingHandler";
39+
private static final String MAPPING_CONTEXT_BEAN_NAME = "neo4jMappingContext";
40+
41+
/*
42+
* (non-Javadoc)
43+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
44+
*/
45+
@Override
46+
protected Class<? extends Annotation> getAnnotation() {
47+
return EnableReactiveNeo4jAuditing.class;
48+
}
49+
50+
/*
51+
* (non-Javadoc)
52+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName()
53+
*/
54+
@Override
55+
protected String getAuditingHandlerBeanName() {
56+
return AUDITING_HANDLER_BEAN_NAME;
57+
}
58+
59+
/*
60+
* (non-Javadoc)
61+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)
62+
*/
63+
@Override
64+
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
65+
BeanDefinitionRegistry registry) {
66+
67+
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
68+
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
69+
70+
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingBeforeBindCallback.class);
71+
72+
builder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
73+
builder.getRawBeanDefinition().setSource(auditingHandlerDefinition.getSource());
74+
75+
registerInfrastructureBeanWithId(builder.getBeanDefinition(), ReactiveAuditingBeforeBindCallback.class.getName(), registry);
76+
}
77+
78+
/*
79+
* (non-Javadoc)
80+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(org.springframework.data.auditing.config.AuditingConfiguration)
81+
*/
82+
@Override
83+
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
84+
85+
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
86+
87+
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveIsNewAwareAuditingHandler.class);
88+
89+
BeanDefinitionBuilder persistentEntities = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntities.class)
90+
.setFactoryMethod("of");
91+
persistentEntities.addConstructorArgReference(MAPPING_CONTEXT_BEAN_NAME);
92+
93+
builder.addConstructorArgValue(persistentEntities.getBeanDefinition());
94+
return configureDefaultAuditHandlerAttributes(configuration, builder);
95+
}
96+
}

src/main/java/org/springframework/data/neo4j/repository/event/ReactiveAuditingBeforeBindCallback.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@
1515
*/
1616
package org.springframework.data.neo4j.repository.event;
1717

18-
import reactor.core.publisher.Mono;
19-
2018
import org.apiguardian.api.API;
2119
import org.reactivestreams.Publisher;
2220
import org.springframework.beans.factory.ObjectFactory;
2321
import org.springframework.core.Ordered;
2422
import org.springframework.data.auditing.AuditingHandler;
25-
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
23+
import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler;
2624
import org.springframework.data.mapping.callback.EntityCallback;
2725
import org.springframework.util.Assert;
2826

@@ -31,23 +29,22 @@
3129
*
3230
* @author Michael J. Simons
3331
* @soundtrack Iron Maiden - The Number Of The Beast
34-
* @see AuditingBeforeBindCallback
3532
* @since 6.0
3633
*/
3734
@API(status = API.Status.INTERNAL, since = "6.0")
3835
public final class ReactiveAuditingBeforeBindCallback implements ReactiveBeforeBindCallback<Object>, Ordered {
3936

4037
public static final int NEO4J_REACTIVE_AUDITING_ORDER = 100;
4138

42-
private final ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory;
39+
private final ObjectFactory<ReactiveIsNewAwareAuditingHandler> auditingHandlerFactory;
4340

4441
/**
4542
* Creates a new {@link ReactiveAuditingBeforeBindCallback} using the {@link AuditingHandler} provided by the given
4643
* {@link ObjectFactory}.
4744
*
4845
* @param auditingHandlerFactory must not be {@literal null}.
4946
*/
50-
public ReactiveAuditingBeforeBindCallback(ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory) {
47+
public ReactiveAuditingBeforeBindCallback(ObjectFactory<ReactiveIsNewAwareAuditingHandler> auditingHandlerFactory) {
5148

5249
Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!");
5350
this.auditingHandlerFactory = auditingHandlerFactory;
@@ -60,7 +57,7 @@ public ReactiveAuditingBeforeBindCallback(ObjectFactory<IsNewAwareAuditingHandle
6057
@Override
6158
public Publisher<Object> onBeforeBind(Object entity) {
6259

63-
return Mono.fromSupplier(() -> auditingHandlerFactory.getObject().markAudited(entity));
60+
return auditingHandlerFactory.getObject().markAudited(entity);
6461
}
6562

6663
/*

src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveAuditingIT.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131
import org.springframework.context.annotation.Bean;
3232
import org.springframework.context.annotation.Configuration;
3333
import org.springframework.data.auditing.DateTimeProvider;
34-
import org.springframework.data.domain.AuditorAware;
34+
import org.springframework.data.domain.ReactiveAuditorAware;
3535
import org.springframework.data.neo4j.config.AbstractReactiveNeo4jConfig;
36-
import org.springframework.data.neo4j.config.EnableNeo4jAuditing;
36+
import org.springframework.data.neo4j.config.EnableReactiveNeo4jAuditing;
3737
import org.springframework.data.neo4j.integration.shared.AuditingITBase;
3838
import org.springframework.data.neo4j.integration.shared.ImmutableAuditableThing;
3939
import org.springframework.data.neo4j.integration.shared.ImmutableAuditableThingWithGeneratedId;
@@ -152,7 +152,7 @@ interface ImmutableEntityWithGeneratedIdTestRepository
152152
@Configuration
153153
@EnableTransactionManagement
154154
@EnableReactiveNeo4jRepositories(considerNestedRepositories = true)
155-
@EnableNeo4jAuditing(modifyOnCreate = false, auditorAwareRef = "auditorProvider",
155+
@EnableReactiveNeo4jAuditing(modifyOnCreate = false, auditorAwareRef = "auditorProvider",
156156
dateTimeProviderRef = "fixedDateTimeProvider")
157157
static class Config extends AbstractReactiveNeo4jConfig {
158158

@@ -162,8 +162,8 @@ public Driver driver() {
162162
}
163163

164164
@Bean
165-
public AuditorAware<String> auditorProvider() {
166-
return () -> Optional.of("A user");
165+
public ReactiveAuditorAware<String> auditorProvider() {
166+
return () -> Mono.just("A user");
167167
}
168168

169169
@Bean

src/test/java/org/springframework/data/neo4j/repository/event/ReactiveAuditingBeforeBindCallbackTest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import static org.mockito.Mockito.times;
2626
import static org.mockito.Mockito.verify;
2727

28+
import reactor.core.publisher.Mono;
2829
import reactor.test.StepVerifier;
2930

3031
import java.util.Arrays;
@@ -33,7 +34,7 @@
3334
import org.junit.jupiter.api.BeforeEach;
3435
import org.junit.jupiter.api.Test;
3536
import org.springframework.core.Ordered;
36-
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
37+
import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler;
3738
import org.springframework.data.mapping.context.PersistentEntities;
3839
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
3940

@@ -42,7 +43,7 @@
4243
*/
4344
class ReactiveAuditingBeforeBindCallbackTest {
4445

45-
IsNewAwareAuditingHandler spyOnHandler;
46+
ReactiveIsNewAwareAuditingHandler spyOnHandler;
4647

4748
ReactiveAuditingBeforeBindCallback callback;
4849

@@ -53,7 +54,7 @@ public void setUp() {
5354
mappingContext.setInitialEntitySet(new HashSet<>(Arrays.asList(Sample.class, ImmutableSample.class)));
5455
mappingContext.initialize();
5556

56-
IsNewAwareAuditingHandler originalHandler = new IsNewAwareAuditingHandler(
57+
ReactiveIsNewAwareAuditingHandler originalHandler = new ReactiveIsNewAwareAuditingHandler(
5758
new PersistentEntities(Arrays.asList(mappingContext)));
5859
spyOnHandler = spy(originalHandler);
5960
callback = new ReactiveAuditingBeforeBindCallback(() -> spyOnHandler);
@@ -107,8 +108,8 @@ void propagatesChangedInstanceToEvent() {
107108
ImmutableSample sample = new ImmutableSample();
108109

109110
ImmutableSample newSample = new ImmutableSample();
110-
IsNewAwareAuditingHandler handler = mock(IsNewAwareAuditingHandler.class);
111-
doReturn(newSample).when(handler).markAudited(eq(sample));
111+
ReactiveIsNewAwareAuditingHandler handler = mock(ReactiveIsNewAwareAuditingHandler.class);
112+
doReturn(Mono.just(newSample)).when(handler).markAudited(eq(sample));
112113

113114
ReactiveAuditingBeforeBindCallback localCallback = new ReactiveAuditingBeforeBindCallback(() -> handler);
114115
StepVerifier.create(localCallback.onBeforeBind(sample)).expectNext(newSample);

0 commit comments

Comments
 (0)