Re-enable support for around advise

This commit is contained in:
Oleg Zhurakousky
2020-10-26 08:29:16 +01:00
parent 3b199d723a
commit 7c860eb334
3 changed files with 85 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2020-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -22,6 +22,12 @@ import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
/** /**
* Wrapper that acts as around advise over function invocation.
* If registered as bean it will be autowired into {@link FunctionInvocationWrapper}.
* Keep in mind that it only affects imperative invocations where input is {@link Message}
*
* NOTE: This API is experimental and and could change without notice. It is
* intended for internal use only (e.g., spring-cloud-sleuth)
* *
* @author Oleg Zhurakousky * @author Oleg Zhurakousky
* @since 3.1 * @since 3.1

View File

@@ -46,6 +46,7 @@ import reactor.core.publisher.Mono;
import reactor.util.function.Tuples; import reactor.util.function.Tuples;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.FunctionCatalog;
import org.springframework.cloud.function.context.FunctionProperties; import org.springframework.cloud.function.context.FunctionProperties;
import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.FunctionRegistration;
@@ -96,6 +97,9 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
private final JsonMapper jsonMapper; private final JsonMapper jsonMapper;
@Autowired(required = false)
private FunctionAroundWrapper functionAroundWrapper;
public SimpleFunctionRegistry(ConversionService conversionService, CompositeMessageConverter messageConverter, JsonMapper jsonMapper) { public SimpleFunctionRegistry(ConversionService conversionService, CompositeMessageConverter messageConverter, JsonMapper jsonMapper) {
Assert.notNull(messageConverter, "'messageConverter' must not be null"); Assert.notNull(messageConverter, "'messageConverter' must not be null");
Assert.notNull(jsonMapper, "'jsonMapper' must not be null"); Assert.notNull(jsonMapper, "'jsonMapper' must not be null");
@@ -106,13 +110,6 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
this.headersField.setAccessible(true); this.headersField.setAccessible(true);
} }
@Override
public FunctionRegistration<?> getRegistration(Object function) {
throw new UnsupportedOperationException("FunctionInspector is deprecated. There is no need "
+ "to access FunctionRegistration directly since you can interogate the actual "
+ "looked-up function (see FunctionInvocationWrapper.");
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <T> T lookup(Class<?> type, String functionDefinition, String... expectedOutputMimeTypes) { public <T> T lookup(Class<?> type, String functionDefinition, String... expectedOutputMimeTypes) {
@@ -129,6 +126,13 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
return (T) function; return (T) function;
} }
@Override
public FunctionRegistration<?> getRegistration(Object function) {
throw new UnsupportedOperationException("FunctionInspector is deprecated. There is no need "
+ "to access FunctionRegistration directly since you can interogate the actual "
+ "looked-up function (see FunctionInvocationWrapper.");
}
@Override @Override
public <T> void register(FunctionRegistration<T> registration) { public <T> void register(FunctionRegistration<T> registration) {
this.functionRegistrations.add(registration); this.functionRegistrations.add(registration);
@@ -168,8 +172,11 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
function.expectedOutputContentType = expectedOutputMimeTypes; function.expectedOutputContentType = expectedOutputMimeTypes;
} }
else { else {
logger.debug("Function '" + functionDefinition + "' is not found"); logger.debug("Function '" + functionDefinition + "' is not found in cache");
} }
function = this.wrapInAroundAviceIfNecessary(function);
return (T) function; return (T) function;
} }
@@ -198,6 +205,25 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
return functionDefinition; return functionDefinition;
} }
/**
* This is primarily to support spring-cloud-sleauth.
* There is no current use cases in functions where it is used.
* The approach may change in the future.
*/
private FunctionInvocationWrapper wrapInAroundAviceIfNecessary(FunctionInvocationWrapper function) {
FunctionInvocationWrapper wrappedFunction = function;
if (function != null && this.functionAroundWrapper != null) {
wrappedFunction = new FunctionInvocationWrapper(function) {
@Override
Object doApply(Object input) {
logger.info("Executing around advise(s)");
return functionAroundWrapper.apply(input, function);
}
};
}
return wrappedFunction;
}
/* /*
* *
*/ */
@@ -259,7 +285,7 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
* *
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public final class FunctionInvocationWrapper implements Function<Object, Object>, Consumer<Object>, Supplier<Object>, Runnable { public class FunctionInvocationWrapper implements Function<Object, Object>, Consumer<Object>, Supplier<Object>, Runnable {
private final Object target; private final Object target;
@@ -288,7 +314,15 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
*/ */
private Function<Object, Message> enhancer; private Function<Object, Message> enhancer;
private FunctionInvocationWrapper(String functionDefinition, Object target, Type inputType, Type outputType) { FunctionInvocationWrapper(FunctionInvocationWrapper function) {
this.target = function.target;
this.inputType = function.inputType;
this.outputType = function.outputType;
this.functionDefinition = function.functionDefinition;
this.message = this.inputType != null && FunctionTypeUtils.isMessage(this.inputType);
}
FunctionInvocationWrapper(String functionDefinition, Object target, Type inputType, Type outputType) {
this.target = target; this.target = target;
this.inputType = this.normalizeType(inputType); this.inputType = this.normalizeType(inputType);
this.outputType = this.normalizeType(outputType); this.outputType = this.normalizeType(outputType);
@@ -558,7 +592,7 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
* *
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Object doApply(Object input) { Object doApply(Object input) {
Object result; Object result;
input = this.fluxifyInputIfNecessary(input); input = this.fluxifyInputIfNecessary(input);

View File

@@ -507,6 +507,16 @@ public class BeanFactoryAwareFunctionRegistryTests {
f.run(); f.run();
} }
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testWrappedWithAroundAdviseConfiguration() {
FunctionCatalog catalog = this.configureCatalog(WrappedWithAroundAdviseConfiguration.class);
Function f = catalog.lookup("uppercase");
Message result = (Message) f.apply(new GenericMessage<String>("hello"));
assertThat(result.getHeaders().get("before")).isEqualTo("foo");
assertThat(result.getHeaders().get("after")).isEqualTo("bar");
}
@EnableAutoConfiguration @EnableAutoConfiguration
public static class PojoToMessageFunctionCompositionConfiguration { public static class PojoToMessageFunctionCompositionConfiguration {
@@ -657,6 +667,29 @@ public class BeanFactoryAwareFunctionRegistryTests {
} }
} }
@EnableAutoConfiguration
@Configuration
protected static class WrappedWithAroundAdviseConfiguration {
@Bean
public Function<Message<String>, Message<String>> uppercase() {
return v -> MessageBuilder.withPayload(v.getPayload().toUpperCase()).copyHeaders(v.getHeaders()).build();
}
@Bean
public FunctionAroundWrapper wrapper() {
return new FunctionAroundWrapper() {
@SuppressWarnings("unchecked")
@Override
protected Object doApply(Message<byte[]> input, FunctionInvocationWrapper targetFunction) {
MessageBuilder.fromMessage(input).setHeader("before", "foo").build();
Message<Object> result = (Message<Object>) targetFunction.apply(MessageBuilder.fromMessage(input).setHeader("before", "foo").build());
return MessageBuilder.fromMessage(result).setHeader("after", "bar").build();
}
};
}
}
@EnableAutoConfiguration @EnableAutoConfiguration
@Configuration @Configuration
protected static class SampleFunctionConfiguration { protected static class SampleFunctionConfiguration {