DATACMNS-1467 - Decouple sync and reactive bits.

Add dedicated interfaces for sync and reactive usage.
Hide default implementation and use reflective callback method lookup.
Update documentation and add initial reference documentation snippet for store specific modules.

Original Pull Request: #332
This commit is contained in:
Christoph Strobl
2019-05-29 14:45:33 +02:00
parent 52724cd5dd
commit dde9a651ba
19 changed files with 1602 additions and 633 deletions

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2019 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
*
* https://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.mapping.callback;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.Ordered;
import org.springframework.data.mapping.Person;
import org.springframework.data.mapping.PersonDocument;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
/**
* @author Christoph Strobl
*/
class CapturingEntityCallback implements EntityCallback<Person> {
final List<Person> captured = new ArrayList<>(3);
final @Nullable Person returnValue;
CapturingEntityCallback() {
this(new PersonDocument(null, null, null));
}
CapturingEntityCallback(@Nullable Person returnValue) {
this.returnValue = returnValue;
}
public Person doSomething(Person person) {
captured.add(person);
return returnValue;
}
Person capturedValue() {
return CollectionUtils.lastElement(captured);
}
List<Person> capturedValues() {
return captured;
}
static class FirstCallback extends CapturingEntityCallback implements Ordered {
@Override
public int getOrder() {
return 1;
}
}
static class SecondCallback extends CapturingEntityCallback implements Ordered {
public SecondCallback() {}
public SecondCallback(Person returnValue) {
super(returnValue);
}
@Override
public int getOrder() {
return 2;
}
}
static class ThirdCallback extends CapturingEntityCallback implements Ordered {
@Override
public int getOrder() {
return 3;
}
}
}

View File

@@ -0,0 +1,291 @@
/*
* Copyright 2019 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
*
* https://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.mapping.callback;
import static org.assertj.core.api.Assertions.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.Ordered;
import org.springframework.data.mapping.Person;
import org.springframework.data.mapping.PersonDocument;
import org.springframework.data.mapping.callback.CapturingEntityCallback.FirstCallback;
import org.springframework.data.mapping.callback.CapturingEntityCallback.SecondCallback;
import org.springframework.data.mapping.callback.CapturingEntityCallback.ThirdCallback;
/**
* Unit tests for {@link DefaultEntityCallbacks}.
*
* @author Mark Paluch
* @author Christoph Strobl
*/
public class DefaultEntityCallbacksUnitTests {
@Test // DATACMNS-1467
public void shouldDispatchCallback() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks(ctx);
PersonDocument personDocument = new PersonDocument(null, "Walter", null);
PersonDocument afterCallback = callbacks.callback(BeforeSaveCallback.class, personDocument);
assertThat(afterCallback.getSsn()).isEqualTo(6);
}
@Test // DATACMNS-1467
public void shouldDispatchCallsToLambdaReceivers() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(LambdaConfig.class);
DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks(ctx);
PersonDocument personDocument = new PersonDocument(null, "Walter", null);
PersonDocument afterCallback = callbacks.callback(BeforeSaveCallback.class, personDocument);
assertThat(afterCallback).isSameAs(personDocument);
}
@Test // DATACMNS-1467
public void invokeGenericEvent() {
DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks();
callbacks.addEntityCallback(new GenericPersonCallback());
Person afterCallback = callbacks.callback(GenericPersonCallback.class, new PersonDocument(null, "Walter", null));
assertThat(afterCallback.getSsn()).isEqualTo(6);
}
@Test // DATACMNS-1467
public void invokeGenericEventWithArgs() {
DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks();
callbacks.addEntityCallback(new GenericPersonCallbackWithArgs());
Person afterCallback = callbacks.callback(GenericPersonCallbackWithArgs.class,
new PersonDocument(null, "Walter", null), "agr0", Float.POSITIVE_INFINITY);
assertThat(afterCallback.getSsn()).isEqualTo(6);
}
@Test // DATACMNS-1467
public void invokeInvalidEvent() {
DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks();
callbacks.addEntityCallback(new InvalidEntityCallback() {});
assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> callbacks.callback(InvalidEntityCallback.class, new PersonDocument(null, "Walter", null),
"agr0", Float.POSITIVE_INFINITY));
}
@Test // DATACMNS-1467
public void passesInvocationResultOnAlongTheChain() {
CapturingEntityCallback first = new FirstCallback();
CapturingEntityCallback second = new SecondCallback();
DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks();
callbacks.addEntityCallback(first);
callbacks.addEntityCallback(second);
PersonDocument initial = new PersonDocument(null, "Walter", null);
callbacks.callback(CapturingEntityCallback.class, initial);
assertThat(first.capturedValue()).isSameAs(initial);
assertThat(first.capturedValues()).hasSize(1);
assertThat(second.capturedValue()).isNotSameAs(initial);
assertThat(second.capturedValues()).hasSize(1);
}
@Test // DATACMNS-1467
public void errorsOnNullEntity() {
DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks();
callbacks.addEntityCallback(new CapturingEntityCallback());
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> callbacks.callback(CapturingEntityCallback.class, null));
}
@Test // DATACMNS-1467
public void errorsOnNullValueReturnedByCallbackEntity() {
CapturingEntityCallback first = new FirstCallback();
CapturingEntityCallback second = new SecondCallback(null);
CapturingEntityCallback third = new ThirdCallback();
DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks();
callbacks.addEntityCallback(first);
callbacks.addEntityCallback(second);
callbacks.addEntityCallback(third);
PersonDocument initial = new PersonDocument(null, "Walter", null);
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> callbacks.callback(CapturingEntityCallback.class, initial));
assertThat(first.capturedValue()).isSameAs(initial);
assertThat(second.capturedValue()).isNotNull().isNotSameAs(initial);
assertThat(third.capturedValues()).isEmpty();
}
@Test // DATACMNS-1467
public void detectsMultipleCallbacksWithinOneClass() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MultipleCallbacksInOneClassConfig.class);
DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks(ctx);
PersonDocument personDocument = new PersonDocument(null, "Walter", null);
callbacks.callback(BeforeSaveCallback.class, personDocument);
assertThat(ctx.getBean("callbacks", MultipleCallbacks.class).invocations).containsExactly("save");
callbacks.callback(BeforeConvertCallback.class, personDocument);
assertThat(ctx.getBean("callbacks", MultipleCallbacks.class).invocations).containsExactly("save", "convert");
}
@Configuration
static class MyConfig {
@Bean
MyBeforeSaveCallback callback() {
return new MyBeforeSaveCallback();
}
@Bean
@Lazy
Object namedCallback() {
return new MyOtherCallback();
}
}
@Configuration
static class LambdaConfig {
@Bean
BeforeSaveCallback<User> userCallback() {
return object -> object;
}
@Bean
BeforeSaveCallback<Person> personCallback() {
return object -> {
object.setSsn(object.getFirstName().length());
return object;
};
}
}
@Configuration
static class MultipleCallbacksInOneClassConfig {
@Bean
MultipleCallbacks callbacks() {
return new MultipleCallbacks();
}
}
interface BeforeConvertCallback<T> extends EntityCallback<T>, Ordered {
T onBeforeConvert(T object);
@Override
default int getOrder() {
return 0;
}
}
interface BeforeSaveCallback<T> extends EntityCallback<T> {
T onBeforeSave(T object);
}
static class MyBeforeSaveCallback implements BeforeSaveCallback<Person> {
@Override
public Person onBeforeSave(Person object) {
object.setSsn(object.getFirstName().length());
return object;
}
}
static class MyOtherCallback implements BeforeSaveCallback<Person> {
@Override
public Person onBeforeSave(Person object) {
return object;
}
}
static class User {}
static class GenericPersonCallback implements EntityCallback<Person> {
public Person onBeforeSave(Person value) {
value.setSsn(value.getFirstName().length());
return value;
}
}
static class GenericPersonCallbackWithArgs implements EntityCallback<Person> {
public Person onBeforeSave(Person value, String agr1, Object arg2) {
value.setSsn(value.getFirstName().length());
return value;
}
}
interface InvalidEntityCallback extends EntityCallback<Person> {
default Person onBeforeSave(String value, Person entity) {
return entity;
}
}
static class MultipleCallbacks implements BeforeConvertCallback<Person>, BeforeSaveCallback<Person> {
List<String> invocations = new ArrayList(2);
@Override
public Person onBeforeConvert(Person object) {
invocations.add("convert");
return object;
}
@Override
public Person onBeforeSave(Person object) {
invocations.add("save");
return object;
}
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright 2019 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
*
* https://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.mapping.callback;
import static org.assertj.core.api.Assertions.*;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mapping.Person;
import org.springframework.data.mapping.PersonDocument;
import org.springframework.data.mapping.callback.CapturingEntityCallback.FirstCallback;
import org.springframework.data.mapping.callback.CapturingEntityCallback.SecondCallback;
import org.springframework.data.mapping.callback.CapturingEntityCallback.ThirdCallback;
/**
* Unit tests for {@link DefaultReactiveEntityCallbacks}.
*
* @author Mark Paluch
* @author Christoph Strobl
*/
public class DefaultReactiveEntityCallbacksUnitTests {
@Test // DATACMNS-1467
public void dispatchResolvesOnSubscribe() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
DefaultReactiveEntityCallbacks callbacks = new DefaultReactiveEntityCallbacks(ctx);
PersonDocument personDocument = new PersonDocument(null, "Walter", null);
Mono<PersonDocument> afterCallback = callbacks.callback(ReactiveBeforeSaveCallback.class, personDocument);
assertThat(personDocument.getSsn()).isNull();
afterCallback.as(StepVerifier::create) //
.consumeNextWith(it -> assertThat(it.getSsn()).isEqualTo(6)) //
.verifyComplete();
}
@Test // DATACMNS-1467
public void invokeGenericEvent() {
DefaultReactiveEntityCallbacks callbacks = new DefaultReactiveEntityCallbacks();
callbacks.addEntityCallback(new GenericPersonCallback());
callbacks.callback(GenericPersonCallback.class, new PersonDocument(null, "Walter", null)) //
.as(StepVerifier::create) //
.consumeNextWith(it -> assertThat(it.getSsn()).isEqualTo(6)) //
.verifyComplete();
}
@Test // DATACMNS-1467
public void passesInvocationResultOnAlongTheChain() {
CapturingEntityCallback first = new FirstCallback();
CapturingEntityCallback second = new SecondCallback();
DefaultReactiveEntityCallbacks callbacks = new DefaultReactiveEntityCallbacks();
callbacks.addEntityCallback(first);
callbacks.addEntityCallback(second);
PersonDocument initial = new PersonDocument(null, "Walter", null);
callbacks.callback(CapturingEntityCallback.class, initial) //
.as(StepVerifier::create) //
.expectNextCount(1) //
.verifyComplete();
assertThat(first.capturedValue()).isSameAs(initial);
assertThat(first.capturedValues()).hasSize(1);
assertThat(second.capturedValue()).isNotSameAs(initial);
assertThat(second.capturedValues()).hasSize(1);
}
@Test // DATACMNS-1467
public void errorsOnNullEntity() {
DefaultReactiveEntityCallbacks callbacks = new DefaultReactiveEntityCallbacks();
callbacks.addEntityCallback(new CapturingEntityCallback());
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> callbacks.callback(CapturingEntityCallback.class, null));
}
@Test // DATACMNS-1467
public void errorsOnNullValueReturnedByCallbackEntity() {
CapturingEntityCallback first = new FirstCallback();
CapturingEntityCallback second = new SecondCallback(null);
CapturingEntityCallback third = new ThirdCallback();
DefaultReactiveEntityCallbacks callbacks = new DefaultReactiveEntityCallbacks();
callbacks.addEntityCallback(first);
callbacks.addEntityCallback(second);
callbacks.addEntityCallback(third);
PersonDocument initial = new PersonDocument(null, "Walter", null);
callbacks.callback(CapturingEntityCallback.class, initial) //
.as(StepVerifier::create) //
.expectError(IllegalArgumentException.class) //
.verify();
assertThat(first.capturedValue()).isSameAs(initial);
assertThat(second.capturedValue()).isNotNull().isNotSameAs(initial);
assertThat(third.capturedValues()).isEmpty();
}
@Configuration
static class MyConfig {
@Bean
MyReactiveBeforeSaveCallback callback() {
return new MyReactiveBeforeSaveCallback();
}
}
interface ReactiveBeforeSaveCallback<T> extends EntityCallback<T> {
Mono<T> onBeforeSave(T object);
}
static class MyReactiveBeforeSaveCallback implements ReactiveBeforeSaveCallback<Person> {
@Override
public Mono<Person> onBeforeSave(Person object) {
PersonDocument result = new PersonDocument(object.getFirstName().length(), object.getFirstName(),
object.getLastName());
return Mono.just(result);
}
}
static class GenericPersonCallback implements EntityCallback<Person> {
public Person onBeforeSave(Person value) {
value.setSsn(value.getFirstName().length());
return value;
}
}
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright 2019 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
*
* https://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.mapping.callback;
import static org.assertj.core.api.Assertions.*;
import java.util.Collection;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.Order;
import org.springframework.data.mapping.Child;
import org.springframework.data.mapping.Person;
import org.springframework.data.mapping.PersonDocument;
/**
* @author Christoph Strobl
*/
public class EntityCallbackDiscovererUnitTests {
@Test // DATACMNS-1467
public void shouldDiscoverCallbackType() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
EntityCallbackDiscoverer discoverer = new EntityCallbackDiscoverer(ctx);
Collection<EntityCallback<Person>> entityCallbacks = discoverer.getEntityCallbacks(PersonDocument.class,
ResolvableType.forType(BeforeSaveCallback.class));
assertThat(entityCallbacks).hasSize(1).element(0).isInstanceOf(MyBeforeSaveCallback.class);
}
@Test // DATACMNS-1467
public void shouldDiscoverCallbackTypeByName() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
EntityCallbackDiscoverer discoverer = new EntityCallbackDiscoverer(ctx);
discoverer.clear();
discoverer.addEntityCallbackBean("namedCallback");
Collection<EntityCallback<Person>> entityCallbacks = discoverer.getEntityCallbacks(PersonDocument.class,
ResolvableType.forType(BeforeSaveCallback.class));
assertThat(entityCallbacks).hasSize(1).element(0).isInstanceOf(MyOtherCallback.class);
}
@Test // DATACMNS-1467
public void shouldSupportCallbackTypes() {
EntityCallbackDiscoverer discoverer = new EntityCallbackDiscoverer();
assertThat(discoverer.supportsEvent(MyBeforeSaveCallback.class, ResolvableType.forClass(Person.class))).isTrue();
assertThat(discoverer.supportsEvent(MyBeforeSaveCallback.class, ResolvableType.forClass(Child.class))).isTrue();
assertThat(discoverer.supportsEvent(BeforeSaveCallback.class, ResolvableType.forClass(PersonDocument.class)))
.isTrue();
assertThat(discoverer.supportsEvent(MyBeforeSaveCallback.class, ResolvableType.forClass(Object.class))).isFalse();
assertThat(discoverer.supportsEvent(MyBeforeSaveCallback.class, ResolvableType.forClass(User.class))).isFalse();
}
@Test // DATACMNS-1467
public void shouldSupportInstanceCallbackTypes() {
EntityCallbackDiscoverer discoverer = new EntityCallbackDiscoverer();
MyBeforeSaveCallback callback = new MyBeforeSaveCallback();
assertThat(discoverer.supportsEvent(callback, ResolvableType.forClass(Person.class),
ResolvableType.forClass(BeforeSaveCallback.class))).isTrue();
assertThat(discoverer.supportsEvent(callback, ResolvableType.forClass(Child.class),
ResolvableType.forClass(BeforeSaveCallback.class))).isTrue();
assertThat(discoverer.supportsEvent(callback, ResolvableType.forClass(PersonDocument.class),
ResolvableType.forClass(BeforeSaveCallback.class))).isTrue();
assertThat(discoverer.supportsEvent(callback, ResolvableType.forClass(PersonDocument.class),
ResolvableType.forClass(BeforeSaveCallback.class))).isTrue();
assertThat(discoverer.supportsEvent(callback, ResolvableType.forClass(User.class),
ResolvableType.forClass(BeforeSaveCallback.class))).isFalse();
}
@Test // DATACMNS-1467
public void shouldDispatchInOrder() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(OrderedConfig.class);
EntityCallbackDiscoverer discoverer = new EntityCallbackDiscoverer(ctx);
Collection<EntityCallback<Person>> entityCallbacks = discoverer.getEntityCallbacks(PersonDocument.class,
ResolvableType.forType(EntityCallback.class));
assertThat(entityCallbacks).containsExactly(ctx.getBean("callback1", EntityCallback.class),
ctx.getBean("callback2", EntityCallback.class), ctx.getBean("callback3", EntityCallback.class),
ctx.getBean("callback4", EntityCallback.class));
}
@Configuration
static class MyConfig {
@Bean
MyBeforeSaveCallback callback() {
return new MyBeforeSaveCallback();
}
@Bean
@Lazy
Object namedCallback() {
return new MyOtherCallback();
}
}
@Configuration
static class OrderedConfig {
@Bean
EntityCallback<Person> callback4() {
return (BeforeSaveCallback<Person>) object -> object;
}
@Bean
EntityCallback<Person> callback2() {
return new Second();
}
@Bean
EntityCallback<Person> callback3() {
return new Third();
}
@Bean
EntityCallback<Person> callback1() {
return new First();
}
@Order(3)
static class Third implements EntityCallback<Person> {
public Person beforeSave(Person object) {
return object;
}
}
static class Second implements EntityCallback<Person>, Ordered {
public Person beforeSave(Person object) {
return object;
}
@Override
public int getOrder() {
return 2;
}
}
@Order(1)
static class First implements EntityCallback<Person> {
public Person beforeSave(Person object) {
return object;
}
}
}
interface BeforeSaveCallback<T> extends EntityCallback<T> {
T onBeforeSave(T object);
}
static class MyBeforeSaveCallback implements BeforeSaveCallback<Person> {
@Override
public Person onBeforeSave(Person object) {
object.setSsn(object.getFirstName().length());
return object;
}
}
static class MyOtherCallback implements BeforeSaveCallback<Person> {
@Override
public Person onBeforeSave(Person object) {
return object;
}
}
static class User {}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2019 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
*
* https://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.mapping.callback;
import static org.assertj.core.api.Assertions.*;
import reactor.core.publisher.Mono;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mapping.Person;
import org.springframework.data.mapping.PersonDocument;
/**
* Unit tests for {@link ReactiveEntityCallbacks}.
*
* @author Mark Paluch
*/
public class ReactiveEntityCallbacksUnitTests {
@Test
public void shouldDispatchCallback() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
ReactiveEntityCallbacks callbacks = new ReactiveEntityCallbacks(ctx);
PersonDocument personDocument = new PersonDocument(null, "Walter", null);
Mono<PersonDocument> afterCallback = callbacks.callbackLater(personDocument, ReactiveBeforeSaveCallback.class,
ReactiveBeforeSaveCallback::onBeforeSave);
assertThat(personDocument.getSsn()).isNull();
assertThat(afterCallback.block().getSsn()).isEqualTo(6);
}
@Configuration
static class MyConfig {
@Bean
MyReactiveBeforeSaveCallback callback() {
return new MyReactiveBeforeSaveCallback();
}
}
static class MyReactiveBeforeSaveCallback implements ReactiveBeforeSaveCallback<Person> {
@Override
public Mono<Person> onBeforeSave(Person object) {
PersonDocument result = new PersonDocument(object.getFirstName().length(), object.getFirstName(),
object.getLastName());
return Mono.just(result);
}
}
}

View File

@@ -1,179 +0,0 @@
/*
* Copyright 2019 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
*
* https://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.mapping.callback;
import static org.assertj.core.api.Assertions.*;
import java.util.Collection;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.ResolvableType;
import org.springframework.data.mapping.Child;
import org.springframework.data.mapping.Person;
import org.springframework.data.mapping.PersonDocument;
/**
* Unit tests for {@link SimpleEntityCallbacks}.
*
* @author Mark Paluch
*/
public class SimpleEntityCallbacksUnitTests {
@Test
public void shouldDispatchCallback() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
SimpleEntityCallbacks callbacks = new SimpleEntityCallbacks(ctx);
PersonDocument personDocument = new PersonDocument(null, "Walter", null);
PersonDocument afterCallback = callbacks.callback(personDocument, BeforeSaveCallback.class,
BeforeSaveCallback::onBeforeSave);
assertThat(afterCallback.getSsn()).isEqualTo(6);
}
@Test
public void shouldDispatchCallsToLambdaReceivers() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(LambdaConfig.class);
SimpleEntityCallbacks callbacks = new SimpleEntityCallbacks(ctx);
PersonDocument personDocument = new PersonDocument(null, "Walter", null);
PersonDocument afterCallback = callbacks.callback(personDocument, BeforeSaveCallback.class,
BeforeSaveCallback::onBeforeSave);
assertThat(afterCallback.getSsn()).isEqualTo(6);
}
@Test
public void shouldDiscoverCallbackType() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
SimpleEntityCallbacks callbacks = new SimpleEntityCallbacks(ctx);
Collection<EntityCallback<?>> entityCallbacks = callbacks.getEntityCallbacks(new PersonDocument(null, null, null),
ResolvableType.forType(BeforeSaveCallback.class));
assertThat(entityCallbacks).hasSize(1).element(0).isInstanceOf(MyBeforeSaveCallback.class);
}
@Test
public void shouldDiscoverCallbackTypeByName() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
SimpleEntityCallbacks callbacks = new SimpleEntityCallbacks(ctx);
callbacks.removeAllCallbacks();
callbacks.addEntityCallbackBean("namedCallback");
Collection<EntityCallback<?>> entityCallbacks = callbacks.getEntityCallbacks(new PersonDocument(null, null, null),
ResolvableType.forType(BeforeSaveCallback.class));
assertThat(entityCallbacks).hasSize(1).element(0).isInstanceOf(MyOtherCallback.class);
}
@Test
public void shouldSupportCallbackTypes() {
SimpleEntityCallbacks callbacks = new SimpleEntityCallbacks();
assertThat(callbacks.supportsEvent(MyBeforeSaveCallback.class, ResolvableType.forClass(Person.class))).isTrue();
assertThat(callbacks.supportsEvent(MyBeforeSaveCallback.class, ResolvableType.forClass(Child.class))).isTrue();
assertThat(callbacks.supportsEvent(BeforeSaveCallback.class, ResolvableType.forClass(PersonDocument.class)))
.isTrue();
assertThat(callbacks.supportsEvent(MyBeforeSaveCallback.class, ResolvableType.forClass(Object.class))).isFalse();
assertThat(callbacks.supportsEvent(MyBeforeSaveCallback.class, ResolvableType.forClass(User.class))).isFalse();
}
@Test
public void shouldSupportInstanceCallbackTypes() {
SimpleEntityCallbacks callbacks = new SimpleEntityCallbacks();
MyBeforeSaveCallback callback = new MyBeforeSaveCallback();
assertThat(callbacks.supportsEvent(callback, ResolvableType.forClass(Person.class),
ResolvableType.forClass(BeforeSaveCallback.class))).isTrue();
assertThat(callbacks.supportsEvent(callback, ResolvableType.forClass(Child.class),
ResolvableType.forClass(BeforeSaveCallback.class))).isTrue();
assertThat(callbacks.supportsEvent(callback, ResolvableType.forClass(PersonDocument.class),
ResolvableType.forClass(BeforeSaveCallback.class))).isTrue();
assertThat(callbacks.supportsEvent(callback, ResolvableType.forClass(PersonDocument.class),
ResolvableType.forClass(BeforeSaveCallback.class))).isTrue();
assertThat(callbacks.supportsEvent(callback, ResolvableType.forClass(User.class),
ResolvableType.forClass(BeforeSaveCallback.class))).isFalse();
}
@Configuration
static class MyConfig {
@Bean
MyBeforeSaveCallback callback() {
return new MyBeforeSaveCallback();
}
@Bean
@Lazy
Object namedCallback() {
return new MyOtherCallback();
}
}
@Configuration
static class LambdaConfig {
@Bean
BeforeSaveCallback<User> userCallback() {
return object -> object;
}
@Bean
BeforeSaveCallback<Person> personCallback() {
return object -> {
object.setSsn(object.getFirstName().length());
return object;
};
}
}
static class MyBeforeSaveCallback implements BeforeSaveCallback<Person> {
@Override
public Person onBeforeSave(Person object) {
object.setSsn(object.getFirstName().length());
return object;
}
}
static class MyOtherCallback implements BeforeSaveCallback<Person> {
@Override
public Person onBeforeSave(Person object) {
return object;
}
}
static class User {}
}