Skip to content

Commit cf1bb02

Browse files
committed
Provide implementation of BeanFactoryNativeConfigurationProcessor for function types
Add initial implementation of FunctionTypeProcessor and FunctionTypeProcessorTests Modified samples to remove TypeHint as well as reference to it in the README Upgrade Spring Cloud dependency to 2021.0.0
1 parent 3b4e691 commit cf1bb02

File tree

7 files changed

+200
-7
lines changed

7 files changed

+200
-7
lines changed

samples/cloud-function-aws/README.md

-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ $> ./build.sh
2626
2727
```
2828

29-
>>NOTE: You will notice we apply `@TypeHint(types = {Person.class}` (see `DemoApplication`). Given that JSON converter will apply reflection to serialize/de-serialize `Person` type for the function input we need to provide minimal reflection configuration and `@TypeHint` will let us do just that.
30-
3129
### Deploy and test
3230

3331
- Navigate to AWS Lambda dashboard and create a new function (name it anyway you want).

samples/cloud-function-aws/src/main/java/com/example/demo/DemoApplication.java

-5
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5-
import org.springframework.nativex.hint.TypeHint;
65

7-
import com.example.demo.domain.Person;
8-
9-
@TypeHint(types = {Person.class})
106
@SpringBootApplication
117
public class DemoApplication {
128

139
public static void main(String[] args) {
1410
SpringApplication.run(DemoApplication.class, args);
1511
}
16-
1712
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

spring-aot/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@
9090
<artifactId>kafka-avro-serializer</artifactId>
9191
<optional>true</optional>
9292
</dependency>
93+
<dependency>
94+
<groupId>org.springframework.cloud</groupId>
95+
<artifactId>spring-cloud-function-context</artifactId>
96+
<optional>true</optional>
97+
</dependency>
9398

9499
<dependency>
95100
<groupId>com.google.code.findbugs</groupId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2021-2021 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+
17+
package org.springframework.cloud.function;
18+
19+
import java.lang.reflect.Type;
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
import java.util.function.Consumer;
23+
import java.util.function.Function;
24+
25+
import org.springframework.aot.context.bootstrap.generator.infrastructure.nativex.BeanFactoryNativeConfigurationProcessor;
26+
import org.springframework.aot.context.bootstrap.generator.infrastructure.nativex.NativeConfigurationRegistry;
27+
import org.springframework.aot.support.BeanFactoryProcessor;
28+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
29+
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
30+
import org.springframework.nativex.hint.TypeAccess;
31+
32+
/**
33+
* Ensures that Function/Consumer input types declared b the user are reflectively available.
34+
*
35+
* @author Oleg Zhurakousky
36+
*
37+
*/
38+
public class FunctionTypeProcessor implements BeanFactoryNativeConfigurationProcessor {
39+
40+
@Override
41+
public void process(ConfigurableListableBeanFactory beanFactory,
42+
NativeConfigurationRegistry registry) {
43+
new Processor().process(beanFactory, registry);
44+
}
45+
46+
private static class Processor {
47+
48+
void process(ConfigurableListableBeanFactory beanFactory, NativeConfigurationRegistry registry) {
49+
final Set<String> added = new HashSet<>();
50+
new BeanFactoryProcessor(beanFactory).processBeans(this::isFunction,
51+
(beanName, functionBeanType) -> {
52+
Type functionType = FunctionTypeUtils.discoverFunctionTypeFromClass(functionBeanType);
53+
Type inputType = FunctionTypeUtils.getInputType(functionType);
54+
String name = inputType.getTypeName();
55+
if (!name.startsWith("java.") &&
56+
!name.startsWith("javax.")) {
57+
if (added.add(name)) {
58+
registry.reflection().forType(FunctionTypeUtils.getRawType(inputType))
59+
.withAccess(TypeAccess.DECLARED_CONSTRUCTORS, TypeAccess.PUBLIC_CONSTRUCTORS, TypeAccess.DECLARED_FIELDS, TypeAccess.PUBLIC_FIELDS, TypeAccess.DECLARED_METHODS, TypeAccess.PUBLIC_METHODS);
60+
}
61+
}
62+
});
63+
}
64+
65+
private boolean isFunction(Class<?> beanType) {
66+
return Function.class.isAssignableFrom(beanType)
67+
|| Consumer.class.isAssignableFrom(beanType);
68+
// we don't care about Suppliers since it's output type is handled by the user code.
69+
}
70+
}
71+
72+
}

spring-aot/src/main/resources/META-INF/spring.factories

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ org.springframework.web.WebNativeConfigurationProcessor,\
3131
org.springframework.core.SynthesizedAnnotationNativeConfigurationProcessor,\
3232
org.springframework.core.IndexedBeanHierarchyNativeConfigurationProcessor,\
3333
org.springframework.context.annotation.ScopeNativeConfigurationProcessor
34+
org.springframework.cloud.function.FunctionTypeProcessor
3435

3536
org.springframework.aot.context.bootstrap.generator.infrastructure.nativex.BeanNativeConfigurationProcessor=\
3637
org.springframework.aot.context.bootstrap.generator.nativex.DefaultBeanNativeConfigurationProcessor,\
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright 2021-2021 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+
17+
package org.springframework.cloud.function;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
21+
import java.util.Arrays;
22+
import java.util.HashSet;
23+
import java.util.List;
24+
import java.util.Set;
25+
import java.util.function.Consumer;
26+
import java.util.function.Function;
27+
import java.util.function.Supplier;
28+
29+
import org.junit.jupiter.api.Test;
30+
import org.springframework.aot.context.bootstrap.generator.infrastructure.nativex.NativeConfigurationRegistry;
31+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
32+
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
33+
import org.springframework.nativex.domain.reflect.ClassDescriptor;
34+
import org.springframework.nativex.hint.TypeAccess;
35+
36+
/**
37+
*
38+
* @author Oleg Zhurakousky
39+
*
40+
*/
41+
class FunctionTypeProcessorTests {
42+
43+
private static Set<TypeAccess> ALL_MEMBERS;
44+
{
45+
ALL_MEMBERS = new HashSet<>(Arrays.asList(TypeAccess.PUBLIC_FIELDS, TypeAccess.DECLARED_FIELDS, TypeAccess.DECLARED_CONSTRUCTORS, TypeAccess.PUBLIC_CONSTRUCTORS, TypeAccess.DECLARED_METHODS, TypeAccess.PUBLIC_METHODS));
46+
}
47+
48+
@Test
49+
void testFunctionTypes() {
50+
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
51+
beanFactory.registerBeanDefinition("noise", BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition());
52+
beanFactory.registerBeanDefinition("sampleFunction", BeanDefinitionBuilder.rootBeanDefinition(MyFunction.class).getBeanDefinition());
53+
NativeConfigurationRegistry registry = process(beanFactory);
54+
List<ClassDescriptor> classDescriptors = registry.reflection().toClassDescriptors();
55+
assertThat(classDescriptors).hasSize(1);
56+
ClassDescriptor cd = classDescriptors.get(0);
57+
assertThat(cd.getName()).isEqualTo(Person.class.getName());
58+
assertThat(cd.getAccess()).containsAll(ALL_MEMBERS);
59+
}
60+
61+
@Test
62+
void testConsumerTypes() {
63+
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
64+
beanFactory.registerBeanDefinition("noise", BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition());
65+
beanFactory.registerBeanDefinition("sampleConsumer", BeanDefinitionBuilder.rootBeanDefinition(MyConsumer.class).getBeanDefinition());
66+
NativeConfigurationRegistry registry = process(beanFactory);
67+
List<ClassDescriptor> classDescriptors = registry.reflection().toClassDescriptors();
68+
assertThat(classDescriptors).hasSize(1);
69+
ClassDescriptor cd = classDescriptors.get(0);
70+
assertThat(cd.getName()).isEqualTo(Person.class.getName());
71+
assertThat(cd.getAccess()).containsAll(ALL_MEMBERS);
72+
}
73+
74+
@Test
75+
void testSupplierTypes() {
76+
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
77+
beanFactory.registerBeanDefinition("noise", BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition());
78+
beanFactory.registerBeanDefinition("sampleSupplier", BeanDefinitionBuilder.rootBeanDefinition(MySupplier.class).getBeanDefinition());
79+
NativeConfigurationRegistry registry = process(beanFactory);
80+
List<ClassDescriptor> classDescriptors = registry.reflection().toClassDescriptors();
81+
assertThat(classDescriptors).hasSize(0);
82+
}
83+
84+
private NativeConfigurationRegistry process(DefaultListableBeanFactory beanFactory) {
85+
NativeConfigurationRegistry registry = new NativeConfigurationRegistry();
86+
new FunctionTypeProcessor().process(beanFactory, registry);
87+
return registry;
88+
}
89+
90+
public static class MyFunction implements Function<Person, String> {
91+
@Override
92+
public String apply(Person t) {
93+
return null;
94+
}
95+
}
96+
97+
public static class MyConsumer implements Consumer<Person> {
98+
@Override
99+
public void accept(Person t) {
100+
}
101+
}
102+
103+
public static class MySupplier implements Supplier<Person> {
104+
@Override
105+
public Person get() {
106+
return null;
107+
}
108+
}
109+
110+
public static class Person {
111+
private String name;
112+
113+
public String getName() {
114+
return name;
115+
}
116+
117+
public void setName(String name) {
118+
this.name = name;
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)