Skip to content

Commit

Permalink
Merge pull request #4 from aboutbits/ab-237-type-ids
Browse files Browse the repository at this point in the history
Ab 237 type ids
  • Loading branch information
SirCotare authored Sep 10, 2024
2 parents 3b9bc51 + 84702e8 commit 19c1c61
Show file tree
Hide file tree
Showing 79 changed files with 3,199 additions and 7 deletions.
99 changes: 95 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
<version>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

Expand All @@ -21,30 +21,121 @@
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>

<!-- Utilities -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<!-- used to scan the classpath -->
<!-- https://mvnrepository.com/artifact/io.github.classgraph/classgraph -->
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>4.8.175</version>
</dependency>


<!-- Validation -->
<!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.9.0</version>
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>

<!-- Swagger -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>

<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>

<!-- Database -->
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>

<!-- Test-Containers -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.20.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.20.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.20.1</version>
<scope>test</scope>
</dependency>
</dependencies>

Expand All @@ -53,7 +144,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
Expand All @@ -62,7 +153,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.2.1</version>
<version>3.5.0</version>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<suppressionsFileExpression>
Expand All @@ -86,7 +177,7 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>10.3.4</version>
<version>10.18.1</version>
</dependency>
</dependencies>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package it.aboutbits.springboot.toolbox.autoconfiguration.persistence;

import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil;
import lombok.SneakyThrows;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.boot.model.TypeContributor;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.descriptor.java.JavaType;

import java.lang.reflect.InvocationTargetException;
import java.util.Set;

public abstract class AbstractCustomTypeContributor implements TypeContributor {
@SuppressWarnings("rawtypes")
private final Set<Class<? extends AutoRegisteredJavaType>> relevantTypes;

protected AbstractCustomTypeContributor(String... packageNames) {
var classScanner = ClassScannerUtil.getScannerForPackages(packageNames);

this.relevantTypes = findAllRelevantTypes(classScanner);
}

@SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class})
@Override
public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
for (var type : relevantTypes) {
typeContributions.contributeJavaType(
(JavaType<?>) type.getConstructors()[0].newInstance()
);
}
}

@SuppressWarnings("rawtypes")
private static Set<Class<? extends AutoRegisteredJavaType>> findAllRelevantTypes(ClassScannerUtil.ClassScanner classScanner) {
return classScanner.getSubTypesOf(AutoRegisteredJavaType.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package it.aboutbits.springboot.toolbox.autoconfiguration.persistence;

import org.hibernate.type.descriptor.java.JavaType;

public interface AutoRegisteredJavaType<T> extends JavaType<T> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package it.aboutbits.springboot.toolbox.autoconfiguration.persistence;

public final class CustomTypeContributor extends AbstractCustomTypeContributor {
public CustomTypeContributor() {
super("it.aboutbits.springboot.toolbox");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package it.aboutbits.springboot.toolbox.autoconfiguration.swagger;

import it.aboutbits.springboot.toolbox.swagger.CustomTypeModelConverter;
import it.aboutbits.springboot.toolbox.swagger.CustomTypePropertyCustomizer;
import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({
CustomTypeModelConverter.class,
CustomTypePropertyCustomizer.class
})
public @interface RegisterCustomTypesWithSwagger {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package it.aboutbits.springboot.toolbox.autoconfiguration.web;

import it.aboutbits.springboot.toolbox.jackson.CustomTypeDeserializer;
import it.aboutbits.springboot.toolbox.jackson.CustomTypeSerializer;
import it.aboutbits.springboot.toolbox.mvc.CustomTypePropertyEditor;
import it.aboutbits.springboot.toolbox.type.CustomType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;

import java.util.Set;

@Slf4j
@Configuration
public class CustomTypeConfiguration {
@ControllerAdvice
public static class CustomTypePropertyBinder {
@SuppressWarnings("rawtypes")
private final Set<Class<? extends CustomType>> types;

public CustomTypePropertyBinder(CustomTypeScanner configuration) {
this.types = configuration.getRelevantTypes();
}

@InitBinder
public void initBinder(WebDataBinder binder) {
for (var clazz : types) {
binder.registerCustomEditor(clazz, new CustomTypePropertyEditor<>(clazz));
}
}
}

@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer(CustomTypeScanner configuration) {
var types = configuration.getRelevantTypes();

var deserializers = types.stream()
.map(CustomTypeDeserializer::new)
.toList()
.toArray(new CustomTypeDeserializer[types.size()]);

return builder -> builder
.serializers(new CustomTypeSerializer())
.deserializers(deserializers);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package it.aboutbits.springboot.toolbox.autoconfiguration.web;

import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil;
import it.aboutbits.springboot.toolbox.type.CustomType;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Getter
public class CustomTypeScanner {
private static final String LIBRARY_BASE_PACKAGE_NAME = "it.aboutbits.springboot.toolbox";

@SuppressWarnings("rawtypes")
private Set<Class<? extends CustomType>> relevantTypes = new HashSet<>();

public void setAdditionalTypePackages(String[] additionalTypePackages) {
var tmp = new ArrayList<String>();
tmp.add(LIBRARY_BASE_PACKAGE_NAME);
tmp.addAll(Arrays.stream(additionalTypePackages)
.filter(item -> !item.isBlank())
.collect(Collectors.toSet()));

var packageNamesToScan = tmp.toArray(new String[0]);

log.info("CustomTypeConfiguration enabled. Scanning: {}", Arrays.toString(packageNamesToScan));
var classScanner = ClassScannerUtil.getScannerForPackages(packageNamesToScan);

this.relevantTypes = findAllCustomTypeRecords(classScanner);
}

@SuppressWarnings("rawtypes")
public static Set<Class<? extends CustomType>> findAllCustomTypeRecords(ClassScannerUtil.ClassScanner classScanner) {
return classScanner.getSubTypesOf(CustomType.class).stream()
.filter(Record.class::isAssignableFrom)
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package it.aboutbits.springboot.toolbox.autoconfiguration.web;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

import java.util.Objects;

public class CustomTypeScannerRegistrar implements ImportBeanDefinitionRegistrar {

/**
* We use this to register a new bean with actual configuration parameters coming from an annotation.
* That way we can use @RegisterCustomTypesWithJacksonAndMvc and take the parameter "additionalTypePackages" to
* scan packages.
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
var attributes = new AnnotationAttributes(
Objects.requireNonNull(
metadata.getAnnotationAttributes(
RegisterCustomTypesWithJacksonAndMvc.class.getName()
)
)
);
var value = attributes.getStringArray("additionalTypePackages");

var builder = BeanDefinitionBuilder.genericBeanDefinition(CustomTypeScanner.class);
builder.addPropertyValue("additionalTypePackages", value);
registry.registerBeanDefinition("CustomTypeScanner", builder.getBeanDefinition());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package it.aboutbits.springboot.toolbox.autoconfiguration.web;

import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({CustomTypeScannerRegistrar.class, CustomTypeConfiguration.class})
public @interface RegisterCustomTypesWithJacksonAndMvc {
String[] additionalTypePackages() default "";
}
Loading

0 comments on commit 19c1c61

Please sign in to comment.