Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.x: Introduction of Helidon Service Inject. #9249

Merged
merged 59 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
cd225a5
Introduction of Helidon Service Inject.
tomas-langer Sep 12, 2024
7df11f0
Remove maven plugin (not yet part of PR), disable test that requires it.
tomas-langer Sep 12, 2024
a6b4380
Build fixes.
tomas-langer Sep 13, 2024
e06fafb
Fixed test to use global config.
tomas-langer Sep 13, 2024
4ea0e78
Attempt to fix test failure in pipelines (does not fail locally).
tomas-langer Sep 13, 2024
7302d13
Renamed `WantDefault` to `AddDefault` for config beans
tomas-langer Sep 16, 2024
228fad8
Codegen annotation processor now correctly returns if it handled all …
tomas-langer Sep 16, 2024
05509bb
Remove Interception.Factory.
tomas-langer Sep 25, 2024
d4d4141
Use existing service descriptor when service registered by contract i…
tomas-langer Sep 26, 2024
8fa8724
Support for meta-annotations in codegen.
tomas-langer Oct 1, 2024
2881350
Scope implementation in registry (javadoc)
tomas-langer Oct 1, 2024
5a8e846
Failing on private field injection, private constructor injection, mu…
tomas-langer Oct 1, 2024
df62b35
Rebased on 4.2.0
tomas-langer Oct 1, 2024
b9415a7
Review fixes.
tomas-langer Oct 1, 2024
0bbad77
Service descriptors moved to be top level classes (backward compatibl…
tomas-langer Oct 2, 2024
ec67a11
TypeInfo can be generated from newly generated classes as well in rou…
tomas-langer Oct 6, 2024
2350086
Removed config beans.
tomas-langer Oct 6, 2024
57c7b1d
Removed main class support.
tomas-langer Oct 6, 2024
91c0d16
Support for delegated interception for service providers, refactoring…
tomas-langer Oct 6, 2024
45efa43
Qualified providers now has provided contract as the first type argum…
tomas-langer Oct 6, 2024
5d480ff
Refactoring of inject
tomas-langer Oct 6, 2024
5aaf585
Codegen changes
tomas-langer Oct 6, 2024
6b541cf
Test changes
tomas-langer Oct 6, 2024
abe4b5e
Spotbugs and javadoc fixes.
tomas-langer Oct 6, 2024
6c2505d
Fix packaging test now that inject main is no longer part of this cha…
tomas-langer Oct 7, 2024
ee8faf2
Rename `NamedByClass` to `NamedByType`
tomas-langer Oct 7, 2024
c69a8c3
Rename `Injection.Instance` to `Injection.PerLookup`
tomas-langer Oct 7, 2024
005ba15
Rename `Injection.CreateFor` to `Injection.PerInstance`
tomas-langer Oct 7, 2024
8de6b72
Rename `Interception.Trigger` to `Interception.Intercepted`
tomas-langer Oct 7, 2024
ac84874
Remaining renaming (full text search)
tomas-langer Oct 7, 2024
064c1a9
Checkstyle fix.
tomas-langer Oct 7, 2024
7eab82f
Parameterized inject support
tomas-langer Oct 7, 2024
296d970
Javadoc and spotbugs fixes.
tomas-langer Oct 7, 2024
972fa58
Fixed handling of wildcards and generics. Added support for bounds (u…
tomas-langer Oct 10, 2024
0f9ce6b
Improved generics support in class model and builder codegen.
tomas-langer Oct 10, 2024
7981348
Copyright fixes.
tomas-langer Oct 10, 2024
52d93b4
Tracing of lookup.
tomas-langer Oct 10, 2024
2767d1f
Bugfixes
tomas-langer Oct 11, 2024
d975d0e
Checkstyle and copyright fixes.
tomas-langer Oct 14, 2024
d388dfa
Mostly working
tomas-langer Oct 15, 2024
74e7b1d
All tests passing
tomas-langer Oct 15, 2024
8647469
Service codegen refactoring:
tomas-langer Oct 15, 2024
9f36253
Javadoc fixes.
tomas-langer Oct 15, 2024
8b2d87e
Native image fix (after moving a type)
tomas-langer Oct 16, 2024
bce576c
Try with fixed Graalvm version
tomas-langer Oct 16, 2024
f0903bb
Use latest checkout action for graalvm
tomas-langer Oct 16, 2024
78a1334
Using the correct graalvm version that matches the 21.0.3 Java version.
tomas-langer Oct 16, 2024
ba1ed9f
Another try, using `java-version` for Graalvm setup
tomas-langer Oct 16, 2024
ae9620e
Documentation update.
tomas-langer Oct 17, 2024
18d1b18
Updated and simplified support for scopes.
tomas-langer Oct 17, 2024
3448165
Checkstyle fix
tomas-langer Oct 17, 2024
b8306b4
Fix interception on methods from abstract classes
tomas-langer Oct 18, 2024
a15670a
Fix decision on delegate interception.
tomas-langer Oct 18, 2024
fe23379
Events
tomas-langer Oct 7, 2024
ea36379
Tmp
tomas-langer Oct 10, 2024
258cf83
Events:
tomas-langer Oct 16, 2024
af44537
Support for events.
tomas-langer Oct 18, 2024
20b2107
Checkstyle and copyright
tomas-langer Oct 18, 2024
95a2de8
ResolvedType no longer implements TypeName to avoid bugs in lookup
tomas-langer Oct 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 2 additions & 3 deletions .github/actions/common/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,9 @@ runs:
git config --global core.eol lf
- name: Set up GraalVM
if: ${{ inputs.native-image == 'true' }}
uses: graalvm/[email protected].1
uses: graalvm/[email protected].4
with:
java-version: ${{ env.JAVA_VERSION }}
version: ${{ env.GRAALVM_VERSION }}
java-version: ${{ env.GRAALVM_VERSION || env.JAVA_VERSION }}
components: ${{ env.GRAALVM_COMPONENTS }}
check-for-updates: 'false'
set-java-home: 'false'
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ on:

env:
JAVA_VERSION: 21
GRAALVM_VERSION: 21.0.3
JAVA_DISTRO: oracle
MAVEN_ARGS: |
-B -fae -e
Expand Down Expand Up @@ -337,7 +338,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-20.04, macos-14 ]
module: [ mp-1, mp-2, mp-3, se-1 ]
module: [ mp-1, mp-2, mp-3, se-1, inject ]
include:
- { os: ubuntu-20.04, platform: linux }
- { os: macos-14, platform: macos }
Expand Down
12 changes: 12 additions & 0 deletions all/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,18 @@
<groupId>io.helidon.service</groupId>
<artifactId>helidon-service-codegen</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.service.inject</groupId>
<artifactId>helidon-service-inject-codegen</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.service.inject</groupId>
<artifactId>helidon-service-inject-api</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.service.inject</groupId>
<artifactId>helidon-service-inject</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.metadata</groupId>
<artifactId>helidon-metadata-hson</artifactId>
Expand Down
15 changes: 15 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1491,6 +1491,21 @@
<artifactId>helidon-service-codegen</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.service.inject</groupId>
<artifactId>helidon-service-inject-codegen</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.service.inject</groupId>
<artifactId>helidon-service-inject-api</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.service.inject</groupId>
<artifactId>helidon-service-inject</artifactId>
<version>${helidon.version}</version>
</dependency>

<!-- Metadata -->
<dependency>
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,17 @@ static FactoryMethods create(CodegenContext ctx,
private static Optional<FactoryMethod> builder(CodegenContext ctx,
TypeHandler typeHandler,
Set<TypeName> builderCandidates) {
if (typeHandler.actualType().equals(OBJECT)) {
if (typeHandler.actualType().equals(OBJECT)
|| typeHandler.actualType().primitive()
|| typeHandler.actualType().generic()) {
return Optional.empty();
}

builderCandidates.add(typeHandler.actualType());
FactoryMethod found = null;
FactoryMethod secondary = null;
for (TypeName builderCandidate : builderCandidates) {
if (typeHandler.actualType().primitive()) {
// primitive methods do not have builders
continue;
}

TypeInfo typeInfo = ctx.typeInfo(builderCandidate.genericTypeName()).orElse(null);
if (typeInfo == null) {
if (secondary == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ static void generate(ClassModel.Builder classModel,
TypeName prototype,
TypeName runtimeType,
List<TypeArgument> typeArguments,
List<TypeName> typeArgumentNames,
TypeContext typeContext) {
Optional<TypeName> superType = typeContext.typeInfo()
.superPrototype();
Expand All @@ -73,7 +74,7 @@ static void generate(ClassModel.Builder classModel,
.description("type of the builder extending this abstract builder")
.bound(TypeName.builder()
.from(TypeName.create(prototype.fqName() + ".BuilderBase"))
.addTypeArguments(typeArguments)
.addTypeArguments(typeArgumentNames)
.addTypeArgument(TypeName.createFromGenericDeclaration("BUILDER"))
.addTypeArgument(TypeName.createFromGenericDeclaration("PROTOTYPE"))
.build()))
Expand Down Expand Up @@ -107,7 +108,7 @@ static void generate(ClassModel.Builder classModel,

// method "from(prototype)"
fromInstanceMethod(builder, typeContext, prototype);
fromBuilderMethod(builder, typeContext, typeArguments);
fromBuilderMethod(builder, typeContext, typeArgumentNames);

// method preBuildPrototype() - handles providers, decorator
preBuildPrototypeMethod(builder, typeContext);
Expand All @@ -126,7 +127,7 @@ static void generate(ClassModel.Builder classModel,
true);

// before the builder class is finished, we also generate a protected implementation
generatePrototypeImpl(builder, typeContext, typeArguments);
generatePrototypeImpl(builder, typeContext, typeArguments, typeArgumentNames);
});
}

Expand Down Expand Up @@ -431,7 +432,8 @@ private static void fromInstanceMethod(InnerClass.Builder builder, TypeContext t

private static void fromBuilderMethod(InnerClass.Builder classBuilder,
TypeContext typeContext,
List<TypeArgument> arguments) {
List<TypeName> arguments) {

TypeName prototype = typeContext.typeInfo().prototype();
TypeName parameterType = TypeName.builder()
.from(TypeName.create(prototype.fqName() + ".BuilderBase"))
Expand Down Expand Up @@ -823,7 +825,8 @@ private static void requiredValidation(Method.Builder validateBuilder, TypeConte

private static void generatePrototypeImpl(InnerClass.Builder classBuilder,
TypeContext typeContext,
List<TypeArgument> typeArguments) {
List<TypeArgument> typeArguments,
List<TypeName> typeArgumentNames) {
Optional<TypeName> superPrototype = typeContext.typeInfo()
.superPrototype();
TypeName prototype = typeContext.typeInfo().prototype();
Expand Down Expand Up @@ -864,7 +867,7 @@ private static void generatePrototypeImpl(InnerClass.Builder classBuilder,
.addParameter(param -> param.name("builder")
.type(TypeName.builder()
.from(TypeName.create(ifaceName + ".BuilderBase"))
.addTypeArguments(typeArguments)
.addTypeArguments(typeArgumentNames)
.addTypeArgument(TypeArgument.create("?"))
.addTypeArgument(TypeArgument.create("?"))
.build())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,21 @@ static void generate(ClassModel.Builder classBuilder,
TypeName prototype,
TypeName runtimeType,
List<TypeArgument> typeArguments,
List<TypeName> typeArgumentNames,
boolean isFactory,
TypeContext typeContext) {
classBuilder.addInnerClass(builder -> {
TypeName builderType = TypeName.builder()
.from(TypeName.create(prototype.fqName() + ".Builder"))
.addTypeArguments(typeArguments)
.addTypeArguments(typeArgumentNames)
.build();
typeArguments.forEach(builder::addGenericArgument);
builder.name("Builder")
.accessModifier(AccessModifier.PACKAGE_PRIVATE)
.description("Fluent API builder for {@link " + runtimeType.className() + "}.")
.superType(TypeName.builder()
.from(TypeName.create(prototype.fqName() + ".BuilderBase"))
.addTypeArguments(typeArguments)
.addTypeArguments(typeArgumentNames)
.addTypeArgument(builderType)
.addTypeArgument(prototype)
.build())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

import io.helidon.codegen.CodegenException;
import io.helidon.codegen.CodegenValidator;
import io.helidon.codegen.classmodel.ContentBuilder;
import io.helidon.codegen.classmodel.Field;
Expand All @@ -38,8 +40,38 @@
import io.helidon.common.types.TypedElementInfo;

import static io.helidon.builder.codegen.Types.OPTION_DEFAULT;
import static io.helidon.common.types.TypeNames.BOXED_BOOLEAN;
import static io.helidon.common.types.TypeNames.BOXED_BYTE;
import static io.helidon.common.types.TypeNames.BOXED_CHAR;
import static io.helidon.common.types.TypeNames.BOXED_DOUBLE;
import static io.helidon.common.types.TypeNames.BOXED_FLOAT;
import static io.helidon.common.types.TypeNames.BOXED_INT;
import static io.helidon.common.types.TypeNames.BOXED_LONG;
import static io.helidon.common.types.TypeNames.BOXED_SHORT;
import static io.helidon.common.types.TypeNames.BOXED_VOID;
import static io.helidon.common.types.TypeNames.PRIMITIVE_BOOLEAN;
import static io.helidon.common.types.TypeNames.PRIMITIVE_BYTE;
import static io.helidon.common.types.TypeNames.PRIMITIVE_CHAR;
import static io.helidon.common.types.TypeNames.PRIMITIVE_DOUBLE;
import static io.helidon.common.types.TypeNames.PRIMITIVE_FLOAT;
import static io.helidon.common.types.TypeNames.PRIMITIVE_INT;
import static io.helidon.common.types.TypeNames.PRIMITIVE_LONG;
import static io.helidon.common.types.TypeNames.PRIMITIVE_SHORT;
import static io.helidon.common.types.TypeNames.PRIMITIVE_VOID;

class TypeHandler {
private static final Map<TypeName, TypeName> BOXED_TO_PRIMITIVE = Map.of(
BOXED_BOOLEAN, PRIMITIVE_BOOLEAN,
BOXED_BYTE, PRIMITIVE_BYTE,
BOXED_SHORT, PRIMITIVE_SHORT,
BOXED_INT, PRIMITIVE_INT,
BOXED_LONG, PRIMITIVE_LONG,
BOXED_CHAR, PRIMITIVE_CHAR,
BOXED_FLOAT, PRIMITIVE_FLOAT,
BOXED_DOUBLE, PRIMITIVE_DOUBLE,
BOXED_VOID, PRIMITIVE_VOID
);

private final TypeName enclosingType;
private final TypedElementInfo annotatedMethod;
private final String name;
Expand Down Expand Up @@ -74,14 +106,18 @@ static TypeHandler create(TypeName blueprintType,
if (TypeNames.SUPPLIER.equals(returnType)) {
return new TypeHandlerSupplier(blueprintType, annotatedMethod, name, getterName, setterName, returnType);
}

if (TypeNames.SET.equals(returnType)) {
checkTypeArgsSizeAndTypes(annotatedMethod, returnType, TypeNames.SET, 1);
return new TypeHandlerSet(blueprintType, annotatedMethod, name, getterName, setterName, returnType);
}

if (TypeNames.LIST.equals(returnType)) {
checkTypeArgsSizeAndTypes(annotatedMethod, returnType, TypeNames.LIST, 1);
return new TypeHandlerList(blueprintType, annotatedMethod, name, getterName, setterName, returnType);
}
if (TypeNames.MAP.equals(returnType)) {
checkTypeArgsSizeAndTypes(annotatedMethod, returnType, TypeNames.MAP, 2);
return new TypeHandlerMap(blueprintType, annotatedMethod, name, getterName, setterName, returnType, sameGeneric);
}

Expand All @@ -96,6 +132,12 @@ static TypeName toWildcard(TypeName typeName) {
if (typeName.wildcard()) {
return typeName;
}
if (typeName.generic()) {
return TypeName.builder()
.className(typeName.className())
.wildcard(true)
.build();
}
return TypeName.builder(typeName).wildcard(true).build();
}

Expand Down Expand Up @@ -524,6 +566,30 @@ protected void declaredSetter(InnerClass.Builder classBuilder,
classBuilder.addMethod(builder);
}

protected TypeName toPrimitive(TypeName typeName) {
return Optional.ofNullable(BOXED_TO_PRIMITIVE.get(typeName))
.orElse(typeName);
}

private static void checkTypeArgsSizeAndTypes(TypedElementInfo annotatedMethod,
TypeName returnType,
TypeName collectionType,
int expectedTypeArgs) {
List<TypeName> typeNames = returnType.typeArguments();
if (typeNames.size() != expectedTypeArgs) {
throw new CodegenException("Property of type " + collectionType.fqName() + " must have " + expectedTypeArgs
+ " type arguments defined",
annotatedMethod.originatingElementValue());
}
for (TypeName typeName : typeNames) {
if (typeName.wildcard()) {
throw new CodegenException("Property of type " + returnType.resolvedName() + " is not supported for builder,"
+ " as wildcards cannot be handled correctly in setters",
annotatedMethod.originatingElementValue());
}
}
}

private <T> T singleDefault(List<T> defaultValues) {
if (defaultValues.isEmpty()) {
throw new IllegalArgumentException("Default values configured for " + name() + " are empty, one value is expected.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,13 @@ String generateMapListFromConfig(FactoryMethods factoryMethods) {

@Override
TypeName argumentTypeName() {
TypeName type = actualType();
if (TypeNames.STRING.equals(type) || toPrimitive(type).primitive() || type.array()) {
return declaredType();
}

return TypeName.builder(collectionType)
.addTypeArgument(toWildcard(actualType()))
.addTypeArgument(toWildcard(type))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,18 @@ void generateFromConfig(Method.Builder method,

@Override
TypeName argumentTypeName() {
TypeName firstType = declaredType().typeArguments().get(0);
if (!(TypeNames.STRING.equals(firstType) || toPrimitive(firstType).primitive() || firstType.array())) {
firstType = toWildcard(firstType);
}
TypeName secondType = declaredType().typeArguments().get(1);
if (!(TypeNames.STRING.equals(secondType) || toPrimitive(secondType).primitive() || secondType.array())) {
secondType = toWildcard(secondType);
}

return TypeName.builder(MAP)
.addTypeArgument(toWildcard(declaredType().typeArguments().get(0)))
.addTypeArgument(toWildcard(declaredType().typeArguments().get(1)))
.addTypeArgument(firstType)
.addTypeArgument(secondType)
.build();
}

Expand Down
Loading