Skip to content

Commit 19c1c61

Browse files
authored
Merge pull request #4 from aboutbits/ab-237-type-ids
Ab 237 type ids
2 parents 3b9bc51 + 84702e8 commit 19c1c61

79 files changed

Lines changed: 3199 additions & 7 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pom.xml

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>org.springframework.boot</groupId>
88
<artifactId>spring-boot-starter-parent</artifactId>
9-
<version>3.3.2</version>
9+
<version>3.3.3</version>
1010
<relativePath/> <!-- lookup parent from repository -->
1111
</parent>
1212

@@ -21,30 +21,121 @@
2121
</properties>
2222

2323
<dependencies>
24+
<dependency>
25+
<groupId>org.springframework.boot</groupId>
26+
<artifactId>spring-boot-starter-data-jpa</artifactId>
27+
</dependency>
28+
2429
<dependency>
2530
<groupId>org.springframework.boot</groupId>
2631
<artifactId>spring-boot-starter-validation</artifactId>
2732
</dependency>
2833

34+
<dependency>
35+
<groupId>org.springframework.boot</groupId>
36+
<artifactId>spring-boot-starter-json</artifactId>
37+
</dependency>
38+
2939
<!-- Utilities -->
3040
<dependency>
3141
<groupId>org.projectlombok</groupId>
3242
<artifactId>lombok</artifactId>
3343
<optional>true</optional>
3444
</dependency>
3545

46+
<!-- used to scan the classpath -->
47+
<!-- https://mvnrepository.com/artifact/io.github.classgraph/classgraph -->
48+
<dependency>
49+
<groupId>io.github.classgraph</groupId>
50+
<artifactId>classgraph</artifactId>
51+
<version>4.8.175</version>
52+
</dependency>
53+
54+
3655
<!-- Validation -->
3756
<!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
3857
<dependency>
3958
<groupId>commons-validator</groupId>
4059
<artifactId>commons-validator</artifactId>
4160
<version>1.9.0</version>
61+
<exclusions>
62+
<exclusion>
63+
<groupId>commons-collections</groupId>
64+
<artifactId>commons-collections</artifactId>
65+
</exclusion>
66+
<exclusion>
67+
<groupId>commons-logging</groupId>
68+
<artifactId>commons-logging</artifactId>
69+
</exclusion>
70+
</exclusions>
71+
</dependency>
72+
73+
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
74+
<dependency>
75+
<groupId>org.apache.commons</groupId>
76+
<artifactId>commons-collections4</artifactId>
77+
<version>4.4</version>
78+
</dependency>
79+
80+
<!-- Swagger -->
81+
<dependency>
82+
<groupId>org.springdoc</groupId>
83+
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
84+
<version>2.6.0</version>
4285
</dependency>
4386

4487
<!-- Testing -->
4588
<dependency>
4689
<groupId>org.springframework.boot</groupId>
4790
<artifactId>spring-boot-starter-test</artifactId>
91+
<scope>test</scope>
92+
</dependency>
93+
<dependency>
94+
<groupId>org.springframework.boot</groupId>
95+
<artifactId>spring-boot-starter-web</artifactId>
96+
<scope>test</scope>
97+
</dependency>
98+
99+
<!-- Database -->
100+
<dependency>
101+
<groupId>org.liquibase</groupId>
102+
<artifactId>liquibase-core</artifactId>
103+
<scope>test</scope>
104+
</dependency>
105+
<dependency>
106+
<groupId>org.postgresql</groupId>
107+
<artifactId>postgresql</artifactId>
108+
<scope>test</scope>
109+
</dependency>
110+
111+
<!-- Test-Containers -->
112+
<dependency>
113+
<groupId>org.testcontainers</groupId>
114+
<artifactId>testcontainers</artifactId>
115+
<version>1.20.1</version>
116+
<scope>test</scope>
117+
<exclusions>
118+
<exclusion>
119+
<groupId>org.hamcrest</groupId>
120+
<artifactId>hamcrest-core</artifactId>
121+
</exclusion>
122+
<exclusion>
123+
<groupId>org.hamcrest</groupId>
124+
<artifactId>hamcrest-library</artifactId>
125+
</exclusion>
126+
</exclusions>
127+
</dependency>
128+
<dependency>
129+
<groupId>org.testcontainers</groupId>
130+
<artifactId>junit-jupiter</artifactId>
131+
<version>1.20.1</version>
132+
<scope>test</scope>
133+
</dependency>
134+
<dependency>
135+
<groupId>org.testcontainers</groupId>
136+
<artifactId>postgresql</artifactId>
137+
<version>1.20.1</version>
138+
<scope>test</scope>
48139
</dependency>
49140
</dependencies>
50141

@@ -53,7 +144,7 @@
53144
<plugin>
54145
<groupId>org.apache.maven.plugins</groupId>
55146
<artifactId>maven-compiler-plugin</artifactId>
56-
<version>3.11.0</version>
147+
<version>3.13.0</version>
57148
<configuration>
58149
<source>${java.version}</source>
59150
<target>${java.version}</target>
@@ -62,7 +153,7 @@
62153
<plugin>
63154
<groupId>org.apache.maven.plugins</groupId>
64155
<artifactId>maven-checkstyle-plugin</artifactId>
65-
<version>3.2.1</version>
156+
<version>3.5.0</version>
66157
<configuration>
67158
<configLocation>checkstyle.xml</configLocation>
68159
<suppressionsFileExpression>
@@ -86,7 +177,7 @@
86177
<dependency>
87178
<groupId>com.puppycrawl.tools</groupId>
88179
<artifactId>checkstyle</artifactId>
89-
<version>10.3.4</version>
180+
<version>10.18.1</version>
90181
</dependency>
91182
</dependencies>
92183
</plugin>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package it.aboutbits.springboot.toolbox.autoconfiguration.persistence;
2+
3+
import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil;
4+
import lombok.SneakyThrows;
5+
import org.hibernate.boot.model.TypeContributions;
6+
import org.hibernate.boot.model.TypeContributor;
7+
import org.hibernate.service.ServiceRegistry;
8+
import org.hibernate.type.descriptor.java.JavaType;
9+
10+
import java.lang.reflect.InvocationTargetException;
11+
import java.util.Set;
12+
13+
public abstract class AbstractCustomTypeContributor implements TypeContributor {
14+
@SuppressWarnings("rawtypes")
15+
private final Set<Class<? extends AutoRegisteredJavaType>> relevantTypes;
16+
17+
protected AbstractCustomTypeContributor(String... packageNames) {
18+
var classScanner = ClassScannerUtil.getScannerForPackages(packageNames);
19+
20+
this.relevantTypes = findAllRelevantTypes(classScanner);
21+
}
22+
23+
@SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class})
24+
@Override
25+
public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
26+
for (var type : relevantTypes) {
27+
typeContributions.contributeJavaType(
28+
(JavaType<?>) type.getConstructors()[0].newInstance()
29+
);
30+
}
31+
}
32+
33+
@SuppressWarnings("rawtypes")
34+
private static Set<Class<? extends AutoRegisteredJavaType>> findAllRelevantTypes(ClassScannerUtil.ClassScanner classScanner) {
35+
return classScanner.getSubTypesOf(AutoRegisteredJavaType.class);
36+
}
37+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package it.aboutbits.springboot.toolbox.autoconfiguration.persistence;
2+
3+
import org.hibernate.type.descriptor.java.JavaType;
4+
5+
public interface AutoRegisteredJavaType<T> extends JavaType<T> {
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package it.aboutbits.springboot.toolbox.autoconfiguration.persistence;
2+
3+
public final class CustomTypeContributor extends AbstractCustomTypeContributor {
4+
public CustomTypeContributor() {
5+
super("it.aboutbits.springboot.toolbox");
6+
}
7+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package it.aboutbits.springboot.toolbox.autoconfiguration.swagger;
2+
3+
import it.aboutbits.springboot.toolbox.swagger.CustomTypeModelConverter;
4+
import it.aboutbits.springboot.toolbox.swagger.CustomTypePropertyCustomizer;
5+
import org.springframework.context.annotation.Import;
6+
7+
import java.lang.annotation.ElementType;
8+
import java.lang.annotation.Retention;
9+
import java.lang.annotation.RetentionPolicy;
10+
import java.lang.annotation.Target;
11+
12+
@Target({ElementType.TYPE})
13+
@Retention(RetentionPolicy.RUNTIME)
14+
@Import({
15+
CustomTypeModelConverter.class,
16+
CustomTypePropertyCustomizer.class
17+
})
18+
public @interface RegisterCustomTypesWithSwagger {
19+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package it.aboutbits.springboot.toolbox.autoconfiguration.web;
2+
3+
import it.aboutbits.springboot.toolbox.jackson.CustomTypeDeserializer;
4+
import it.aboutbits.springboot.toolbox.jackson.CustomTypeSerializer;
5+
import it.aboutbits.springboot.toolbox.mvc.CustomTypePropertyEditor;
6+
import it.aboutbits.springboot.toolbox.type.CustomType;
7+
import lombok.extern.slf4j.Slf4j;
8+
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
9+
import org.springframework.context.annotation.Bean;
10+
import org.springframework.context.annotation.Configuration;
11+
import org.springframework.web.bind.WebDataBinder;
12+
import org.springframework.web.bind.annotation.ControllerAdvice;
13+
import org.springframework.web.bind.annotation.InitBinder;
14+
15+
import java.util.Set;
16+
17+
@Slf4j
18+
@Configuration
19+
public class CustomTypeConfiguration {
20+
@ControllerAdvice
21+
public static class CustomTypePropertyBinder {
22+
@SuppressWarnings("rawtypes")
23+
private final Set<Class<? extends CustomType>> types;
24+
25+
public CustomTypePropertyBinder(CustomTypeScanner configuration) {
26+
this.types = configuration.getRelevantTypes();
27+
}
28+
29+
@InitBinder
30+
public void initBinder(WebDataBinder binder) {
31+
for (var clazz : types) {
32+
binder.registerCustomEditor(clazz, new CustomTypePropertyEditor<>(clazz));
33+
}
34+
}
35+
}
36+
37+
@Bean
38+
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer(CustomTypeScanner configuration) {
39+
var types = configuration.getRelevantTypes();
40+
41+
var deserializers = types.stream()
42+
.map(CustomTypeDeserializer::new)
43+
.toList()
44+
.toArray(new CustomTypeDeserializer[types.size()]);
45+
46+
return builder -> builder
47+
.serializers(new CustomTypeSerializer())
48+
.deserializers(deserializers);
49+
}
50+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package it.aboutbits.springboot.toolbox.autoconfiguration.web;
2+
3+
import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil;
4+
import it.aboutbits.springboot.toolbox.type.CustomType;
5+
import lombok.Getter;
6+
import lombok.extern.slf4j.Slf4j;
7+
8+
import java.util.ArrayList;
9+
import java.util.Arrays;
10+
import java.util.HashSet;
11+
import java.util.Set;
12+
import java.util.stream.Collectors;
13+
14+
@Slf4j
15+
@Getter
16+
public class CustomTypeScanner {
17+
private static final String LIBRARY_BASE_PACKAGE_NAME = "it.aboutbits.springboot.toolbox";
18+
19+
@SuppressWarnings("rawtypes")
20+
private Set<Class<? extends CustomType>> relevantTypes = new HashSet<>();
21+
22+
public void setAdditionalTypePackages(String[] additionalTypePackages) {
23+
var tmp = new ArrayList<String>();
24+
tmp.add(LIBRARY_BASE_PACKAGE_NAME);
25+
tmp.addAll(Arrays.stream(additionalTypePackages)
26+
.filter(item -> !item.isBlank())
27+
.collect(Collectors.toSet()));
28+
29+
var packageNamesToScan = tmp.toArray(new String[0]);
30+
31+
log.info("CustomTypeConfiguration enabled. Scanning: {}", Arrays.toString(packageNamesToScan));
32+
var classScanner = ClassScannerUtil.getScannerForPackages(packageNamesToScan);
33+
34+
this.relevantTypes = findAllCustomTypeRecords(classScanner);
35+
}
36+
37+
@SuppressWarnings("rawtypes")
38+
public static Set<Class<? extends CustomType>> findAllCustomTypeRecords(ClassScannerUtil.ClassScanner classScanner) {
39+
return classScanner.getSubTypesOf(CustomType.class).stream()
40+
.filter(Record.class::isAssignableFrom)
41+
.collect(Collectors.toSet());
42+
}
43+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package it.aboutbits.springboot.toolbox.autoconfiguration.web;
2+
3+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
4+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
5+
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
6+
import org.springframework.core.annotation.AnnotationAttributes;
7+
import org.springframework.core.type.AnnotationMetadata;
8+
9+
import java.util.Objects;
10+
11+
public class CustomTypeScannerRegistrar implements ImportBeanDefinitionRegistrar {
12+
13+
/**
14+
* We use this to register a new bean with actual configuration parameters coming from an annotation.
15+
* That way we can use @RegisterCustomTypesWithJacksonAndMvc and take the parameter "additionalTypePackages" to
16+
* scan packages.
17+
*/
18+
@Override
19+
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
20+
var attributes = new AnnotationAttributes(
21+
Objects.requireNonNull(
22+
metadata.getAnnotationAttributes(
23+
RegisterCustomTypesWithJacksonAndMvc.class.getName()
24+
)
25+
)
26+
);
27+
var value = attributes.getStringArray("additionalTypePackages");
28+
29+
var builder = BeanDefinitionBuilder.genericBeanDefinition(CustomTypeScanner.class);
30+
builder.addPropertyValue("additionalTypePackages", value);
31+
registry.registerBeanDefinition("CustomTypeScanner", builder.getBeanDefinition());
32+
}
33+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package it.aboutbits.springboot.toolbox.autoconfiguration.web;
2+
3+
import org.springframework.context.annotation.Import;
4+
5+
import java.lang.annotation.ElementType;
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.RetentionPolicy;
8+
import java.lang.annotation.Target;
9+
10+
@Target({ElementType.TYPE})
11+
@Retention(RetentionPolicy.RUNTIME)
12+
@Import({CustomTypeScannerRegistrar.class, CustomTypeConfiguration.class})
13+
public @interface RegisterCustomTypesWithJacksonAndMvc {
14+
String[] additionalTypePackages() default "";
15+
}

0 commit comments

Comments
 (0)