Skip to content

Commit 67f5aa3

Browse files
authored
4.x: Set all contracts when calling Services.set (helidon-io#10816)
* Added a new method to descriptor to analyze types at runtime, no change to codegen. * Added a test * Services.set() now uses all contracts associated with the used type * Support for add method and set with qualiers to use all interfaces * Update documentation of services.
1 parent 73ba7a6 commit 67f5aa3

File tree

12 files changed

+551
-229
lines changed

12 files changed

+551
-229
lines changed

codegen/class-model/src/main/java/io/helidon/codegen/classmodel/ClassBase.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -223,9 +223,14 @@ void writeComponent(ModelWriter writer, Set<String> declaredTokens, ImportOrgani
223223
}
224224
writer.write("{");
225225
writer.writeSeparatorLine();
226+
226227
if (!staticFields.isEmpty()) {
227228
writeClassFields(staticFields, writer, combinedTokens, imports);
228229
}
230+
231+
// support for static initializers
232+
writePostConstantDeclaration(writer, declaredTokens, imports, classType);
233+
229234
if (!fields.isEmpty()) {
230235
writeClassFields(fields, writer, combinedTokens, imports);
231236
}
@@ -245,6 +250,11 @@ void writeComponent(ModelWriter writer, Set<String> declaredTokens, ImportOrgani
245250
writer.write("}");
246251
}
247252

253+
void writePostConstantDeclaration(ModelWriter writer, Set<String> declaredTokens, ImportOrganizer imports, ClassType classType)
254+
throws IOException {
255+
// default impl does nothing, as interfaces do not support this
256+
}
257+
248258
@Override
249259
void addImports(ImportOrganizer.Builder imports) {
250260
super.addImports(imports);

codegen/class-model/src/main/java/io/helidon/codegen/classmodel/ClassModel.java

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import java.util.Map;
2222
import java.util.Optional;
2323
import java.util.Set;
24+
import java.util.function.Consumer;
2425

2526
import io.helidon.common.types.AccessModifier;
2627
import io.helidon.common.types.TypeName;
@@ -49,12 +50,15 @@ public final class ClassModel extends ClassBase {
4950
public static final String DEFAULT_PADDING = " ";
5051
private final String packageName;
5152
private final String copyright;
53+
private final Content staticInitializer;
54+
5255
private ImportOrganizer imports;
5356

5457
private ClassModel(Builder builder) {
5558
super(builder);
5659
this.copyright = builder.copyright;
5760
this.packageName = builder.packageName;
61+
this.staticInitializer = builder.staticInitializer == null ? null : builder.staticInitializer.build();
5862
}
5963

6064
/**
@@ -128,6 +132,23 @@ void writeComponent(ModelWriter writer,
128132
writer.writeSeparatorLine();
129133
}
130134

135+
@Override
136+
void writePostConstantDeclaration(ModelWriter writer, Set<String> declaredTokens, ImportOrganizer imports, ClassType classType)
137+
throws IOException {
138+
if (staticInitializer == null) {
139+
return;
140+
}
141+
writer.increasePaddingLevel();
142+
writer.write("\n");
143+
writer.increasePaddingLevel();
144+
writer.writeLine("static {");
145+
staticInitializer.writeBody(writer, imports);
146+
writer.decreasePaddingLevel();
147+
writer.write("\n");
148+
writer.writeLine("}");
149+
writer.decreasePaddingLevel();
150+
}
151+
131152
/**
132153
* Type name of this class.
133154
*
@@ -145,11 +166,19 @@ public String toString() {
145166
+ '}';
146167
}
147168

169+
@Override
170+
void addImports(ImportOrganizer.Builder builder) {
171+
super.addImports(builder);
172+
if (staticInitializer != null) {
173+
staticInitializer.addImports(builder);
174+
}
175+
}
176+
148177
/**
149178
* Fluent API builder for {@link ClassModel}.
150179
*/
151180
public static final class Builder extends ClassBase.Builder<Builder, ClassModel> {
152-
181+
private Content.Builder staticInitializer;
153182
private String packageName = "";
154183
private String copyright;
155184

@@ -212,6 +241,20 @@ public Builder type(TypeName type) {
212241
return this;
213242
}
214243

244+
/**
245+
* Update the static initializer of this class.
246+
*
247+
* @param contentBuilder content builder consumer to update the static initializer
248+
* @return updated builder instance
249+
*/
250+
public Builder staticInitializer(Consumer<ContentBuilder<?>> contentBuilder) {
251+
this.staticInitializer = (this.staticInitializer == null)
252+
? Content.builder()
253+
: this.staticInitializer;
254+
contentBuilder.accept(staticInitializer);
255+
return this;
256+
}
257+
215258
/**
216259
* Find if the provided type name is handled as part of this generated class.
217260
*

service/codegen/src/main/java/io/helidon/service/codegen/DescribedService.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,16 @@ static DescribedService create(RegistryCodegenContext ctx,
185185
processedDirectContracts,
186186
it));
187187

188+
Map<ResolvedType, Set<ResolvedType>> contractMap = new HashMap<>();
189+
directContracts.forEach(directContract -> addContractToMap(roundContext,
190+
serviceContracts,
191+
contractMap,
192+
directContract));
193+
providedContracts.forEach(providedContract -> addContractToMap(roundContext,
194+
serviceContracts,
195+
contractMap,
196+
providedContract));
197+
188198
DescribedType serviceDescriptor;
189199
DescribedType providedDescriptor;
190200

@@ -193,6 +203,7 @@ static DescribedService create(RegistryCodegenContext ctx,
193203
serviceDescriptor = new DescribedType(serviceInfo,
194204
serviceInfo.typeName(),
195205
directContracts,
206+
contractMap,
196207
serviceElements);
197208

198209
providedDescriptor = null;
@@ -201,12 +212,14 @@ static DescribedService create(RegistryCodegenContext ctx,
201212
serviceDescriptor = new DescribedType(serviceInfo,
202213
serviceInfo.typeName(),
203214
directContracts,
215+
contractMap,
204216
serviceElements);
205217
DescribedElements providedElements = DescribedElements.create(ctx, interception, providedContracts, providedTypeInfo);
206218

207219
providedDescriptor = new DescribedType(providedTypeInfo,
208220
providedTypeName,
209221
providedContracts,
222+
contractMap,
210223
providedElements);
211224
}
212225

@@ -222,6 +235,24 @@ static DescribedService create(RegistryCodegenContext ctx,
222235
);
223236
}
224237

238+
private static void addContractToMap(RegistryRoundContext ctx,
239+
ServiceContracts serviceContracts,
240+
Map<ResolvedType, Set<ResolvedType>> contractMap,
241+
ResolvedType contract) {
242+
243+
var transitiveContracts = contractMap.computeIfAbsent(contract, k -> new HashSet<>());
244+
transitiveContracts.add(contract);
245+
246+
// contract may have type arguments that are using real types, whereas the typeInfo may have generics - we need to
247+
// remember the correct generic types
248+
var maybeTypeInfo = ctx.typeInfo(contract.type());
249+
if (maybeTypeInfo.isEmpty()) {
250+
// we cannot add, as not on classpath
251+
return;
252+
}
253+
serviceContracts.addContracts(transitiveContracts, new HashSet<>(), maybeTypeInfo.get());
254+
}
255+
225256
@Override
226257
public String toString() {
227258
return serviceType.typeName().fqName();

service/codegen/src/main/java/io/helidon/service/codegen/DescribedType.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2024, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package io.helidon.service.codegen;
1818

19+
import java.util.Map;
1920
import java.util.Objects;
2021
import java.util.Set;
2122

@@ -29,27 +30,43 @@
2930
* A described type (class, interface).
3031
* User service can have up to two types - one is the service itself, another one is a provided contract,
3132
* if the service is a provider.
33+
*
34+
* @deprecated this class is not part of public API and does not have any public elements, it will be package private in
35+
* a future release
3236
*/
37+
@Deprecated(forRemoval = true, since = "4.4.0")
3338
public class DescribedType {
3439
private final TypeInfo typeInfo;
3540
private final boolean isAbstract;
3641
private final TypeName typeName;
3742
private final Set<ResolvedType> contracts;
3843
private final DescribedElements elements;
44+
private final Map<ResolvedType, Set<ResolvedType>> contractTypes;
45+
46+
DescribedType(TypeInfo typeInfo,
47+
TypeName typeName,
48+
Set<ResolvedType> contracts,
49+
Map<ResolvedType, Set<ResolvedType>> contractMap,
50+
DescribedElements elements) {
3951

40-
DescribedType(TypeInfo typeInfo, TypeName typeName, Set<ResolvedType> contracts, DescribedElements elements) {
4152
Objects.requireNonNull(typeInfo);
4253
Objects.requireNonNull(typeName);
4354
Objects.requireNonNull(contracts);
55+
Objects.requireNonNull(contractMap);
4456
Objects.requireNonNull(elements);
4557

4658
this.typeInfo = typeInfo;
4759
this.isAbstract = isAbstract(typeInfo);
4860
this.typeName = typeName;
4961
this.contracts = contracts;
62+
this.contractTypes = contractMap;
5063
this.elements = elements;
5164
}
5265

66+
Map<ResolvedType, Set<ResolvedType>> contractTypeSets() {
67+
return contractTypes;
68+
}
69+
5370
boolean isAbstract() {
5471
return isAbstract;
5572
}

service/codegen/src/main/java/io/helidon/service/codegen/ServiceCodegenTypes.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,14 @@ public final class ServiceCodegenTypes {
368368
public static final TypeName SET_OF_RESOLVED_TYPES = TypeName.builder(TypeNames.SET)
369369
.addTypeArgument(TypeNames.RESOLVED_TYPE_NAME)
370370
.build();
371+
372+
/**
373+
* A map of resolved types to a set of resolved types.
374+
*/
375+
public static final TypeName MAP_RESOLVED_TO_SET_OF_RESOLVED = TypeName.builder(TypeNames.MAP)
376+
.addTypeArgument(TypeNames.RESOLVED_TYPE_NAME)
377+
.addTypeArgument(SET_OF_RESOLVED_TYPES)
378+
.build();
371379
/**
372380
* A set of String.
373381
*/
@@ -402,5 +410,3 @@ public final class ServiceCodegenTypes {
402410
private ServiceCodegenTypes() {
403411
}
404412
}
405-
406-

0 commit comments

Comments
 (0)