GH-265 Added support for injecting execution context

- Added support for injecting target execution context into the ApplicationContext in AbstractSpringFunctionAdapterInitializer, effectively making it available to all adapters (if one is provided)
- Added test, docs

Resolves #265
This commit is contained in:
Oleg Zhurakousky
2019-03-01 17:07:03 +01:00
parent 3f4401f9be
commit d20171d196
3 changed files with 47 additions and 2 deletions

View File

@@ -22,6 +22,25 @@ This Azure handler will delegate to a `Function<Foo,Bar>` bean (or a `Function<P
If your app has more than one `@Bean` of type `Function` etc. then you can choose the one to use by configuring `function.name`. Or if you make the `@FunctionName` in the Azure handler method match the function name it should work that way (also for function apps with multiple functions). The functions are extracted from the Spring Cloud `FunctionCatalog` so the default function names are the same as the bean names.
=== Accessing Azure ExecutionContext
Some time there is a need to access the target execution context provided by Azure runtime in the form of `com.microsoft.azure.functions.ExecutionContext`.
For example one of such needs is logging, so it can appear in the Azure console.
For that purpose Spring Cloud Function will register `ExecutionContext` as bean in the Application context, so it could be injected into your function.
For example
```java
@Bean
public Function<Foo, Bar> uppercase(ExecutionContext targetContext) {
return foo -> {
targetContext.getLogger().info("Invoking 'uppercase' on " + foo.getValue());
return new Bar(foo.getValue().toUpperCase());
};
}
```
Normally type-based injection should suffice, however if need to you can also utilise the bean name under which it is registered which is `targetExecutionContext`.
=== Notes on JAR Layout
You don't need the Spring Cloud Function Web at runtime in Azure, so you can exclude this before you create the JAR you deploy to Azure, but it won't be used if you include it so it doesn't hurt to leave it in. A function application on Azure is an archive generated by the Maven plugin. The function lives in the JAR file generated by this project. The sample creates it as an executable jar, using the thin layout, so that Azure can find the handler classes. If you prefer you can just use a regular flat JAR file. The dependencies should *not* be included.

View File

@@ -22,6 +22,7 @@ import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.microsoft.azure.functions.ExecutionContext;
import org.junit.After;
import org.junit.Test;
import reactor.core.publisher.Flux;
@@ -151,8 +152,11 @@ public class AzureSpringBootRequestHandlerTests {
protected static class AutoConfig {
@Bean
public Function<Foo, Bar> uppercase() {
return foo -> new Bar(foo.getValue().toUpperCase());
public Function<Foo, Bar> uppercase(ExecutionContext targetContext) {
return foo -> {
targetContext.getLogger().info("Invoking 'uppercase' on " + foo.getValue());
return new Bar(foo.getValue().toUpperCase());
};
}
}

View File

@@ -42,7 +42,9 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.cloud.function.context.catalog.FunctionInspector;
import org.springframework.cloud.function.context.config.FunctionContextUtils;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.ClassUtils;
/**
@@ -57,6 +59,11 @@ public abstract class AbstractSpringFunctionAdapterInitializer<C> implements Clo
private static Log logger = LogFactory.getLog(AbstractSpringFunctionAdapterInitializer.class);
/**
* Name of the bean for registering the target execution context passed to `initialize(context)` operation.
*/
public static final String TARGET_EXECUTION_CTX_BEAN_NAME = "targetExecutionContext";
private final Class<?> configurationClass;
private Function<Publisher<?>, Publisher<?>> function;
@@ -102,6 +109,8 @@ public abstract class AbstractSpringFunctionAdapterInitializer<C> implements Clo
}
logger.info("Initializing: " + this.configurationClass);
SpringApplication builder = springApplication();
this.registerTargetContext(targetContext, builder);
ConfigurableApplicationContext context = builder.run();
context.getAutowireCapableBeanFactory().autowireBean(this);
this.context = context;
@@ -113,6 +122,19 @@ public abstract class AbstractSpringFunctionAdapterInitializer<C> implements Clo
}
}
private void registerTargetContext(C targetContext, SpringApplication builder) {
if (targetContext != null) {
builder.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@SuppressWarnings("unchecked")
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
((GenericApplicationContext) applicationContext).registerBean(TARGET_EXECUTION_CTX_BEAN_NAME,
(Class<C>) targetContext.getClass(), (Supplier<C>) () -> targetContext);
}
});
}
}
protected FunctionInspector getInspector() {
return inspector;
}