├── .github
└── workflows
│ └── maven.yml
├── .gitignore
├── README.md
├── api
├── .gitignore
├── pom.xml
└── src
│ └── main
│ └── java
│ └── org
│ └── soujava
│ └── medatadata
│ └── api
│ ├── ClassMappings.java
│ ├── Column.java
│ ├── Constructor.java
│ ├── Entity.java
│ ├── EntityMetadata.java
│ ├── FieldMetadata.java
│ ├── Id.java
│ ├── Mapper.java
│ ├── MapperException.java
│ └── Param.java
├── compile
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── org
│ │ │ └── soujava
│ │ │ └── metadata
│ │ │ └── compiler
│ │ │ ├── CompileMapper.java
│ │ │ ├── CompilerAccessException.java
│ │ │ ├── EntityMetadata.java
│ │ │ ├── EntityMetadataFactory.java
│ │ │ ├── FieldMetadata.java
│ │ │ ├── FieldReader.java
│ │ │ ├── FieldReaderFactory.java
│ │ │ ├── FieldWriter.java
│ │ │ ├── FieldWriterFactory.java
│ │ │ ├── GeneratedJavaFileManager.java
│ │ │ ├── InstanceSupplier.java
│ │ │ ├── InstanceSupplierFactory.java
│ │ │ ├── JavaCompiledStream.java
│ │ │ ├── JavaCompilerClassLoader.java
│ │ │ ├── JavaCompilerFacade.java
│ │ │ ├── JavaFileObject.java
│ │ │ ├── JavaSource.java
│ │ │ ├── StringFormatter.java
│ │ │ └── TemplateReader.java
│ └── resources
│ │ ├── FieldReader.tempalte
│ │ ├── FieldWriter.template
│ │ └── InstanceSupplier.template
│ └── test
│ └── java
│ └── org
│ └── soujava
│ └── metadata
│ └── compiler
│ ├── Animal.java
│ ├── CompilerMapperTest.java
│ ├── CompilerTest.java
│ ├── JavaCompilerBeanPropertyReaderFactory.java
│ └── ReadFromGetterMethod.java
├── example
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── org
│ │ └── soujava
│ │ └── metadata
│ │ └── example
│ │ ├── Animal.java
│ │ ├── Car.java
│ │ └── Person.java
│ └── test
│ └── java
│ └── org
│ └── soujava
│ └── metadata
│ └── example
│ ├── AbstractMapperTest.java
│ ├── CarTest.java
│ ├── CompilerMapTest.java
│ ├── PersonTest.java
│ ├── ProcessorMapTest.java
│ └── ReflectionMapTest.java
├── pom.xml
├── processor
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── org
│ │ │ └── soujava
│ │ │ └── metadata
│ │ │ └── processor
│ │ │ ├── BaseMappingModel.java
│ │ │ ├── ClassAnalyzer.java
│ │ │ ├── ClassMappingsModel.java
│ │ │ ├── EntityModel.java
│ │ │ ├── EntityProcessor.java
│ │ │ ├── FieldAnalyzer.java
│ │ │ ├── FieldModel.java
│ │ │ └── ProcessorUtil.java
│ └── resources
│ │ ├── META-INF
│ │ └── services
│ │ │ └── javax.annotation.processing.Processor
│ │ ├── ProcessorMap.java
│ │ ├── classmappings.mustache
│ │ ├── entitymetadata.mustache
│ │ └── fieldmetadata.mustache
│ └── test
│ ├── java
│ └── org
│ │ └── soujava
│ │ └── metadata
│ │ └── processor
│ │ └── CompilerTest.java
│ └── resources
│ ├── Person.java
│ ├── Person2.java
│ ├── Person3.java
│ ├── Person4.java
│ ├── Person5.java
│ ├── Person6.java
│ ├── Person7.java
│ └── Person8.java
└── reflection
├── .gitignore
├── pom.xml
└── src
├── main
└── java
│ └── org
│ └── soujava
│ └── metadata
│ └── reflection
│ ├── EntitySupplier.java
│ ├── FieldReader.java
│ ├── FieldWriter.java
│ └── ReflectionMapper.java
└── test
└── java
└── org
└── soujava
└── metadata
└── reflection
├── Animal.java
├── Person.java
└── ReflectionMapperTest.java
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3 |
4 | name: Java 21 CI with Maven
5 |
6 | on:
7 | push:
8 | branches: [ main ]
9 | pull_request:
10 | branches: [ main ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Set up JDK 21
20 | uses: actions/setup-java@v3
21 | with:
22 | distribution: 'temurin'
23 | java-version: 21
24 |
25 | - name: Cache local Maven repository
26 | uses: actions/cache@v3
27 | with:
28 | path: ~/.m2/repository
29 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
30 | restore-keys: |
31 | ${{ runner.os }}-maven
32 | - name: Build with Maven
33 | run: mvn -B package --file pom.xml
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | pom.xml.tag
3 | pom.xml.releaseBackup
4 | pom.xml.versionsBackup
5 | pom.xml.next
6 | test-output/
7 | /doc
8 | *.iml
9 | *.idea
10 | *.log
11 | /.idea
12 | .checkstyle
13 |
14 | # Eclipse metadata
15 | .settings/
16 | .project
17 | .factorypath
18 | .classpath
19 | -project
20 | /.resourceCache
21 | /.project
22 |
23 | # Annotation processor metadata
24 | .apt_generated/
25 | .apt_generated_tests/
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Modern Cloud-Native Jakarta EE Frameworks: tips, challenges, and trends
2 |
3 | This part demonstrates the generation of Java source code.
4 |
5 | The example requires Java 8 and Maven >= 3.
6 |
7 | Compile the Project:
8 |
9 | ```
10 | mvn clean install
11 | ```
12 |
13 | ## Articles
14 |
15 | * [Portuguese](https://www.infoq.com/br/articles/frameworks-java-na-era-cloud-native-java-Jakarta/)
16 | * [English](https://dzone.com/articles/modern-cloud-native-jakarta-ee-frameworks)
17 | * [French](https://www.infoq.com/fr/articles/java-frameworks-cloud-native/)
18 |
--------------------------------------------------------------------------------
/api/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | pom.xml.tag
3 | pom.xml.releaseBackup
4 | pom.xml.versionsBackup
5 | pom.xml.next
6 | test-output/
7 | /doc
8 | *.iml
9 | *.idea
10 | *.log
11 | /.idea
12 | .checkstyle
13 |
14 | # Eclipse metadata
15 | .settings/
16 | .project
17 | .factorypath
18 | .classpath
19 | -project
20 | /.resourceCache
21 | /.project
22 |
23 | # Annotation processor metadata
24 | .apt_generated/
25 | .apt_generated_tests/
26 |
--------------------------------------------------------------------------------
/api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 |
8 |
9 | org.soujava.metadata
10 | parent
11 | 1.0.0
12 |
13 | api
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/api/src/main/java/org/soujava/medatadata/api/ClassMappings.java:
--------------------------------------------------------------------------------
1 | package org.soujava.medatadata.api;
2 |
3 | import java.util.Optional;
4 |
5 | public interface ClassMappings {
6 |
7 | EntityMetadata get(Class> classEntity);
8 |
9 | EntityMetadata findByName(String name);
10 |
11 | Optional findBySimpleName(String name);
12 |
13 | Optional findByClassName(String name);
14 | }
15 |
--------------------------------------------------------------------------------
/api/src/main/java/org/soujava/medatadata/api/Column.java:
--------------------------------------------------------------------------------
1 | package org.soujava.medatadata.api;
2 |
3 |
4 | import java.lang.annotation.Documented;
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 | @Documented
11 | @Target(ElementType.FIELD)
12 | @Retention(RetentionPolicy.RUNTIME)
13 | public @interface Column {
14 | String value() default "";
15 | }
16 |
--------------------------------------------------------------------------------
/api/src/main/java/org/soujava/medatadata/api/Constructor.java:
--------------------------------------------------------------------------------
1 | package org.soujava.medatadata.api;
2 |
3 |
4 | import java.lang.annotation.Documented;
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 | @Documented
11 | @Target(ElementType.CONSTRUCTOR)
12 | @Retention(RetentionPolicy.RUNTIME)
13 | public @interface Constructor {
14 | }
15 |
--------------------------------------------------------------------------------
/api/src/main/java/org/soujava/medatadata/api/Entity.java:
--------------------------------------------------------------------------------
1 | package org.soujava.medatadata.api;
2 |
3 |
4 | import java.lang.annotation.Documented;
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 | @Documented
11 | @Target(ElementType.TYPE)
12 | @Retention(RetentionPolicy.RUNTIME)
13 | public @interface Entity {
14 | String value() default "";
15 | }
16 |
--------------------------------------------------------------------------------
/api/src/main/java/org/soujava/medatadata/api/EntityMetadata.java:
--------------------------------------------------------------------------------
1 | package org.soujava.medatadata.api;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 | import java.util.Optional;
6 |
7 | public interface EntityMetadata {
8 |
9 | String getName();
10 |
11 | String getSimpleName();
12 |
13 | String getClassName();
14 |
15 | List getFieldsName();
16 |
17 | Class> getClassInstance();
18 |
19 | List getFields();
20 |
21 | T newInstance();
22 |
23 | String getColumnField(String javaField);
24 |
25 | Optional getFieldMapping(String javaField);
26 |
27 | Map getFieldsGroupByName();
28 |
29 | Optional getId();
30 | }
31 |
--------------------------------------------------------------------------------
/api/src/main/java/org/soujava/medatadata/api/FieldMetadata.java:
--------------------------------------------------------------------------------
1 | package org.soujava.medatadata.api;
2 |
3 | import java.util.Set;
4 |
5 | public interface FieldMetadata {
6 |
7 | boolean isId();
8 |
9 | String getFieldName();
10 |
11 | String getName();
12 |
13 | void write(Object bean, Object value);
14 |
15 | Object read(Object bean);
16 |
17 | Class> getType();
18 |
19 | Set> getArguments();
20 | }
21 |
--------------------------------------------------------------------------------
/api/src/main/java/org/soujava/medatadata/api/Id.java:
--------------------------------------------------------------------------------
1 | package org.soujava.medatadata.api;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | @Documented
10 | @Target(ElementType.FIELD)
11 | @Retention(RetentionPolicy.RUNTIME)
12 | public @interface Id {
13 |
14 | String value() default "";
15 | }
16 |
--------------------------------------------------------------------------------
/api/src/main/java/org/soujava/medatadata/api/Mapper.java:
--------------------------------------------------------------------------------
1 | package org.soujava.medatadata.api;
2 |
3 | import java.util.Map;
4 |
5 | public interface Mapper {
6 |
7 | T toEntity(Map map, Class type);
8 |
9 | Map toMap(T entity);
10 | }
11 |
--------------------------------------------------------------------------------
/api/src/main/java/org/soujava/medatadata/api/MapperException.java:
--------------------------------------------------------------------------------
1 | package org.soujava.medatadata.api;
2 |
3 | public class MapperException extends RuntimeException {
4 |
5 | public MapperException(String message) {
6 | super(message);
7 | }
8 |
9 | public MapperException(String message, Throwable cause) {
10 | super(message, cause);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/api/src/main/java/org/soujava/medatadata/api/Param.java:
--------------------------------------------------------------------------------
1 | package org.soujava.medatadata.api;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Retention(RetentionPolicy.RUNTIME)
9 | @Target(ElementType.PARAMETER)
10 | public @interface Param {
11 |
12 | String value();
13 | }
--------------------------------------------------------------------------------
/compile/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | pom.xml.tag
3 | pom.xml.releaseBackup
4 | pom.xml.versionsBackup
5 | pom.xml.next
6 | test-output/
7 | /doc
8 | *.iml
9 | *.idea
10 | *.log
11 | /.idea
12 | .checkstyle
13 |
14 | # Eclipse metadata
15 | .settings/
16 | .project
17 | .factorypath
18 | .classpath
19 | -project
20 | /.resourceCache
21 | /.project
22 |
23 | # Annotation processor metadata
24 | .apt_generated/
25 | .apt_generated_tests/
26 |
--------------------------------------------------------------------------------
/compile/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 |
8 |
9 | org.soujava.metadata
10 | parent
11 | 1.0.0
12 |
13 | compile
14 |
15 |
16 |
17 | ${project.groupId}
18 | api
19 | ${project.version}
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/CompileMapper.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 | import org.soujava.medatadata.api.Mapper;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.Objects;
8 | import java.util.function.Function;
9 | import java.util.stream.Collectors;
10 |
11 | public class CompileMapper implements Mapper {
12 |
13 | private Map, EntityMetadata> mapper = new HashMap<>();
14 | private final EntityMetadataFactory factory = new EntityMetadataFactory();
15 |
16 | @Override
17 | public T toEntity(Map map, Class type) {
18 | Objects.requireNonNull(map, "Map is required");
19 | Objects.requireNonNull(type, "type is required");
20 |
21 | final EntityMetadata entityMetadata = getEntityMetadata(type);
22 | final T instance = entityMetadata.newInstance();
23 | final Map fieldMap = entityMetadata.getFields().stream()
24 | .collect(Collectors.toMap(FieldMetadata::getName, Function.identity()));
25 |
26 | for (Map.Entry entry : map.entrySet()) {
27 | final FieldMetadata metadata = fieldMap.get(entry.getKey());
28 | if (metadata != null) {
29 | metadata.write(instance, entry.getValue());
30 | }
31 | }
32 |
33 | return instance;
34 | }
35 |
36 | @Override
37 | public Map toMap(T entity) {
38 | Objects.requireNonNull(entity, "entity is required");
39 | final EntityMetadata entityMetadata = getEntityMetadata(entity.getClass());
40 | Map map = new HashMap<>();
41 | map.put("entity", entityMetadata.getName());
42 | for (FieldMetadata field : entityMetadata.getFields()) {
43 | final Object value = field.getValue(entity);
44 | if (value != null) {
45 | map.put(field.getName(), value);
46 | }
47 | }
48 | return map;
49 | }
50 |
51 | private EntityMetadata getEntityMetadata(Class type) {
52 | final EntityMetadata entityMetadata = mapper.get(type);
53 | if (entityMetadata != null) {
54 | return entityMetadata;
55 | }
56 | synchronized (type) {
57 | final EntityMetadata entity = factory.apply(type);
58 | mapper.put(type, entity);
59 | return entity;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/CompilerAccessException.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 | public class CompilerAccessException extends RuntimeException{
4 |
5 | public CompilerAccessException(String message, Throwable cause) {
6 | super(message, cause);
7 | }
8 |
9 | public CompilerAccessException(String message) {
10 | super(message);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/EntityMetadata.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 | import org.soujava.medatadata.api.Entity;
4 |
5 | import java.util.List;
6 |
7 | class EntityMetadata {
8 |
9 | private final Class> type;
10 |
11 | private final InstanceSupplier supplier;
12 |
13 | private final List fields;
14 |
15 | EntityMetadata(Class> type, InstanceSupplier supplier, List fields) {
16 | this.type = type;
17 | this.supplier = supplier;
18 | this.fields = fields;
19 | }
20 |
21 |
22 | public Class> getType() {
23 | return type;
24 | }
25 |
26 | public T newInstance() {
27 | return (T) this.supplier.get();
28 | }
29 |
30 | public List getFields() {
31 | return fields;
32 | }
33 |
34 | public String getName() {
35 | final Entity annotation = type.getAnnotation(Entity.class);
36 | final String value = annotation.value();
37 | return value.isEmpty() ? type.getSimpleName() : value;
38 | }
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/EntityMetadataFactory.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 | import org.soujava.medatadata.api.Column;
4 | import org.soujava.medatadata.api.Id;
5 |
6 | import java.lang.reflect.Field;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 | import java.util.function.Function;
10 |
11 | class EntityMetadataFactory implements Function, EntityMetadata> {
12 |
13 | private final JavaCompilerFacade compilerFacade;
14 | private final FieldReaderFactory readerFactory;
15 | private final FieldWriterFactory writerFactory;
16 | private final InstanceSupplierFactory instanceFactory;
17 |
18 | public EntityMetadataFactory() {
19 | this.compilerFacade = new JavaCompilerFacade(EntityMetadataFactory.class.getClassLoader());
20 | this.readerFactory = new FieldReaderFactory(compilerFacade);
21 | this.writerFactory = new FieldWriterFactory(compilerFacade);
22 | this.instanceFactory = new InstanceSupplierFactory(compilerFacade);
23 | }
24 |
25 | @Override
26 | public EntityMetadata apply(Class> type) {
27 |
28 | List fields = new ArrayList<>();
29 | for (Field field : type.getDeclaredFields()) {
30 | final Id id = field.getAnnotation(Id.class);
31 | final Column column = field.getAnnotation(Column.class);
32 | if (id != null || column != null) {
33 | final FieldReader reader = readerFactory.apply(field);
34 | final FieldWriter writer = writerFactory.apply(field);
35 | fields.add(new FieldMetadata(field, reader, writer));
36 | }
37 | }
38 | final InstanceSupplier supplier = instanceFactory.apply(type.getConstructors()[0]);
39 | return new EntityMetadata(type, supplier, fields);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/FieldMetadata.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 | import org.soujava.medatadata.api.Column;
4 | import org.soujava.medatadata.api.Id;
5 |
6 | import java.lang.reflect.Field;
7 |
8 | class FieldMetadata {
9 |
10 | private final Field field;
11 |
12 | private final FieldReader reader;
13 |
14 | private final FieldWriter writer;
15 |
16 | FieldMetadata(Field field, FieldReader reader, FieldWriter writer) {
17 | this.field = field;
18 | this.reader = reader;
19 | this.writer = writer;
20 | }
21 |
22 |
23 | public String getName() {
24 | final Id id = field.getAnnotation(Id.class);
25 | final Column column = field.getAnnotation(Column.class);
26 | if (id != null) {
27 | return id.value().isEmpty() ? field.getName() : id.value();
28 | } else if (column != null) {
29 | return column.value().isEmpty() ? field.getName() : column.value();
30 | }
31 | return field.getName();
32 | }
33 |
34 | public Object getValue(T entity) {
35 | return this.reader.read(entity);
36 | }
37 |
38 | public void write(T entity, Object value) {
39 | this.writer.write(entity, value);
40 | }
41 |
42 | @Override
43 | public String toString() {
44 | return "FieldMetadata{" +
45 | "field=" + field +
46 | ", reader=" + reader +
47 | ", writer=" + writer +
48 | '}';
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/FieldReader.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 | public interface FieldReader {
4 |
5 | Object read(Object bean);
6 | }
7 |
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/FieldReaderFactory.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 | import java.beans.PropertyDescriptor;
4 | import java.lang.reflect.Field;
5 | import java.lang.reflect.Method;
6 | import java.lang.reflect.Modifier;
7 | import java.util.Optional;
8 | import java.util.function.Function;
9 | import java.util.logging.Level;
10 | import java.util.logging.Logger;
11 |
12 |
13 | class FieldReaderFactory {
14 |
15 | private static final Logger LOGGER = Logger.getLogger(FieldReaderFactory.class.getName());
16 |
17 | private static final String TEMPLATE_FILE = "FieldReader.tempalte";
18 |
19 | private static final String TEMPLATE = TemplateReader.INSTANCE.apply(TEMPLATE_FILE);
20 |
21 | private final JavaCompilerFacade compilerFacade;
22 |
23 | FieldReaderFactory(JavaCompilerFacade compilerFacade) {
24 | this.compilerFacade = compilerFacade;
25 | }
26 |
27 |
28 | public FieldReader apply(Field field) {
29 |
30 | Class> declaringClass = field.getDeclaringClass();
31 | Optional methodName = getMethodName(declaringClass, field);
32 |
33 | return methodName.map(compile(declaringClass))
34 | .orElseThrow(() -> new RuntimeException("there is an issue to compile a FieldReader"));
35 |
36 | }
37 |
38 | private Function compile(Class> declaringClass) {
39 | return method -> {
40 | String packageName = declaringClass.getPackage().getName();
41 |
42 | String simpleName = declaringClass.getSimpleName() + "$" + method;
43 | String newInstance = declaringClass.getName();
44 | String name = declaringClass.getName() + "$" + method;
45 | String javaSource = StringFormatter.INSTANCE.format(TEMPLATE, packageName, simpleName, newInstance, method);
46 | FieldReaderJavaSource source = new FieldReaderJavaSource(name, simpleName, javaSource);
47 | Optional> reader = compilerFacade.apply(source);
48 | return reader.map(this::newInstance).orElse(null);
49 | };
50 | }
51 |
52 | private FieldReader newInstance(Class extends FieldReader> readerClass) {
53 | try {
54 | return (FieldReader) readerClass.getConstructors()[0].newInstance();
55 | } catch (Exception e) {
56 | throw new RuntimeException("An issue to create a new instance class", e);
57 | }
58 | }
59 |
60 | private Optional getMethodName(Class> declaringClass, Field field) {
61 | try {
62 | Method readMethod = new PropertyDescriptor(field.getName(), declaringClass).getReadMethod();
63 | if (Modifier.isPublic(readMethod.getModifiers())) {
64 | return Optional.of(readMethod.getName());
65 | }
66 | } catch (Exception e) {
67 | LOGGER.log(Level.FINE, "A getter method does not exist to the field: "
68 | + field.getName() + " within class " + declaringClass.getName() + " using the fallback with reflection", e);
69 |
70 | }
71 | return Optional.empty();
72 | }
73 |
74 | private static final class FieldReaderJavaSource implements JavaSource {
75 |
76 | private final String name;
77 |
78 | private final String simpleName;
79 |
80 | private final String javaSource;
81 |
82 |
83 | FieldReaderJavaSource(String name, String simpleName, String javaSource) {
84 | this.name = name;
85 | this.simpleName = simpleName;
86 | this.javaSource = javaSource;
87 | }
88 |
89 | @Override
90 | public String getSimpleName() {
91 | return simpleName;
92 | }
93 |
94 | @Override
95 | public String getName() {
96 | return name;
97 | }
98 |
99 | @Override
100 | public String getJavaSource() {
101 | return javaSource;
102 | }
103 |
104 | @Override
105 | public Class getType() {
106 | return FieldReader.class;
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/FieldWriter.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 | public interface FieldWriter {
4 |
5 | void write(Object bean, Object value);
6 | }
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/FieldWriterFactory.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 | import java.beans.PropertyDescriptor;
4 | import java.lang.reflect.Field;
5 | import java.lang.reflect.Method;
6 | import java.lang.reflect.Modifier;
7 | import java.util.Optional;
8 | import java.util.function.Function;
9 | import java.util.logging.Level;
10 | import java.util.logging.Logger;
11 |
12 | class FieldWriterFactory {
13 |
14 | private static final Logger LOGGER = Logger.getLogger(FieldWriterFactory.class.getName());
15 |
16 | private static final String TEMPLATE_FILE = "FieldWriter.template";
17 |
18 | private static final String TEMPLATE = TemplateReader.INSTANCE.apply(TEMPLATE_FILE);
19 |
20 | private final JavaCompilerFacade compilerFacade;
21 |
22 | FieldWriterFactory(JavaCompilerFacade compilerFacade) {
23 | this.compilerFacade = compilerFacade;
24 | }
25 |
26 |
27 | public FieldWriter apply(Field field) {
28 |
29 | Class> declaringClass = field.getDeclaringClass();
30 | Optional methodName = getMethodName(declaringClass, field);
31 | return methodName.map(compile(declaringClass, field.getType()))
32 | .orElseThrow(() -> new RuntimeException("there is an issue to compile a FieldReader"));
33 | }
34 |
35 | private Function compile(Class> declaringClass, Class> type) {
36 | return method -> {
37 | String packageName = declaringClass.getPackage().getName();
38 | String simpleName = declaringClass.getSimpleName() + "$" + method;
39 | String newInstance = declaringClass.getName();
40 | String name = declaringClass.getName() + "$" + method;
41 | String typeCast = type.getName();
42 | String javaSource = StringFormatter.INSTANCE.format(TEMPLATE, packageName, simpleName,
43 | newInstance, method, typeCast);
44 |
45 | FieldWriterJavaSource source = new FieldWriterJavaSource(name, simpleName, javaSource);
46 | Optional> reader = compilerFacade.apply(source);
47 | return reader.map(this::newInstance).orElse(null);
48 | };
49 | }
50 |
51 | private FieldWriter newInstance(Class extends FieldWriter> writerClass) {
52 | try {
53 | return (FieldWriter) writerClass.getConstructors()[0].newInstance();
54 | } catch (Exception e) {
55 | throw new RuntimeException("An issue to create a new instance class", e);
56 | }
57 | }
58 |
59 | private Optional getMethodName(Class> declaringClass, Field field) {
60 | try {
61 | Method writeMethod = new PropertyDescriptor(field.getName(), declaringClass).getWriteMethod();
62 | if (Modifier.isPublic(writeMethod.getModifiers())) {
63 | return Optional.of(writeMethod.getName());
64 | }
65 | } catch (Exception e) {
66 | LOGGER.log(Level.FINE, "A setter method does not exist to the field: "
67 | + field.getName() + " within class " + declaringClass.getName() + " using the fallback with reflection", e);
68 |
69 | }
70 | return Optional.empty();
71 | }
72 |
73 | private static final class FieldWriterJavaSource implements JavaSource {
74 |
75 | private final String name;
76 |
77 | private final String simpleName;
78 |
79 | private final String javaSource;
80 |
81 |
82 | FieldWriterJavaSource(String name, String simpleName, String javaSource) {
83 | this.name = name;
84 | this.simpleName = simpleName;
85 | this.javaSource = javaSource;
86 | }
87 |
88 | @Override
89 | public String getSimpleName() {
90 | return simpleName;
91 | }
92 |
93 | @Override
94 | public String getName() {
95 | return name;
96 | }
97 |
98 | @Override
99 | public String getJavaSource() {
100 | return javaSource;
101 | }
102 |
103 | @Override
104 | public Class getType() {
105 | return FieldWriter.class;
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/GeneratedJavaFileManager.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 |
4 | import javax.tools.FileObject;
5 | import javax.tools.ForwardingJavaFileManager;
6 | import javax.tools.JavaFileManager;
7 | import javax.tools.JavaFileObject;
8 | import javax.tools.JavaFileObject.Kind;
9 |
10 | final class GeneratedJavaFileManager extends ForwardingJavaFileManager {
11 |
12 | private final JavaCompilerClassLoader classLoader;
13 |
14 | public GeneratedJavaFileManager(JavaFileManager fileManager, JavaCompilerClassLoader classLoader) {
15 | super(fileManager);
16 | this.classLoader = classLoader;
17 | }
18 |
19 | @Override
20 | public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, Kind kind, FileObject sibling) {
21 | if (kind != Kind.CLASS) {
22 | throw new IllegalArgumentException("Unsupported kind (" + kind + ") for class (" + qualifiedName + ").");
23 | }
24 | JavaCompiledStream fileObject = new JavaCompiledStream(qualifiedName);
25 | classLoader.addJavaFileObject(qualifiedName, fileObject);
26 | return fileObject;
27 | }
28 |
29 | @Override
30 | public ClassLoader getClassLoader(Location location) {
31 | return classLoader;
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/compile/src/main/java/org/soujava/metadata/compiler/InstanceSupplier.java:
--------------------------------------------------------------------------------
1 | package org.soujava.metadata.compiler;
2 |
3 | import java.util.function.Supplier;
4 |
5 | public interface InstanceSupplier extends Supplier