Skip to content

Commit 0659ec7

Browse files
authored
Use Helidon metadata format (HSON) for service registry generated file. (#9061)
Add format description. Add module that can be used both from code generator and from runtime to read the service registry metadata.
1 parent 35eb5ac commit 0659ec7

File tree

27 files changed

+668
-217
lines changed

27 files changed

+668
-217
lines changed

all/pom.xml

+4
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,10 @@
10901090
<groupId>io.helidon.inject.configdriven</groupId>
10911091
<artifactId>helidon-inject-configdriven-processor</artifactId>
10921092
</dependency>
1093+
<dependency>
1094+
<groupId>io.helidon.service</groupId>
1095+
<artifactId>helidon-service-metadata</artifactId>
1096+
</dependency>
10931097
<dependency>
10941098
<groupId>io.helidon.service</groupId>
10951099
<artifactId>helidon-service-registry</artifactId>

bom/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -1436,6 +1436,11 @@
14361436
</dependency>
14371437

14381438
<!-- Service registry -->
1439+
<dependency>
1440+
<groupId>io.helidon.service</groupId>
1441+
<artifactId>helidon-service-metadata</artifactId>
1442+
<version>${helidon.version}</version>
1443+
</dependency>
14391444
<dependency>
14401445
<groupId>io.helidon.service</groupId>
14411446
<artifactId>helidon-service-registry</artifactId>

codegen/apt/src/main/java/io/helidon/codegen/apt/AptFiler.java

+16
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package io.helidon.codegen.apt;
1818

1919
import java.io.BufferedReader;
20+
import java.io.ByteArrayOutputStream;
2021
import java.io.IOException;
2122
import java.io.InputStreamReader;
2223
import java.io.OutputStream;
@@ -35,6 +36,7 @@
3536
import io.helidon.codegen.CodegenException;
3637
import io.helidon.codegen.CodegenFiler;
3738
import io.helidon.codegen.CodegenOptions;
39+
import io.helidon.codegen.FilerResource;
3840
import io.helidon.codegen.FilerTextResource;
3941
import io.helidon.codegen.IndentType;
4042
import io.helidon.codegen.classmodel.ClassModel;
@@ -107,6 +109,20 @@ public FilerTextResource textResource(String location, Object... originatingElem
107109
}
108110
}
109111

112+
@Override
113+
public FilerResource resource(String location, Object... originatingElements) {
114+
try {
115+
var resource = filer.getResource(StandardLocation.CLASS_OUTPUT, "", location);
116+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
117+
try (var is = resource.openInputStream()) {
118+
is.transferTo(baos);
119+
}
120+
return new FilerResourceImpl(filer, location, toElements(originatingElements), resource, baos.toByteArray());
121+
} catch (IOException e) {
122+
return new FilerResourceImpl(filer, location, toElements(originatingElements));
123+
}
124+
}
125+
110126
private Object originatingElement(Element[] elements, Object alternative) {
111127
if (elements.length == 0) {
112128
return alternative;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.codegen.apt;
18+
19+
import java.util.Arrays;
20+
21+
import javax.annotation.processing.Filer;
22+
import javax.lang.model.element.Element;
23+
import javax.tools.FileObject;
24+
import javax.tools.StandardLocation;
25+
26+
import io.helidon.codegen.CodegenException;
27+
import io.helidon.codegen.FilerResource;
28+
29+
class FilerResourceImpl implements FilerResource {
30+
private final Filer filer;
31+
private final String location;
32+
private final Element[] originatingElements;
33+
private final FileObject originalResource; // may be null
34+
35+
private byte[] currentBytes;
36+
37+
private boolean modified;
38+
39+
FilerResourceImpl(Filer filer, String location, Element[] originatingElements) {
40+
this.filer = filer;
41+
this.location = location;
42+
this.originatingElements = originatingElements;
43+
this.originalResource = null;
44+
this.currentBytes = new byte[0];
45+
}
46+
47+
FilerResourceImpl(Filer filer,
48+
String location,
49+
Element[] originatingElements,
50+
FileObject originalResource,
51+
byte[] existingBytes) {
52+
this.filer = filer;
53+
this.location = location;
54+
this.originatingElements = originatingElements;
55+
this.originalResource = originalResource;
56+
this.currentBytes = existingBytes;
57+
}
58+
59+
@Override
60+
public byte[] bytes() {
61+
return Arrays.copyOf(currentBytes, currentBytes.length);
62+
}
63+
64+
@Override
65+
public void bytes(byte[] newBytes) {
66+
currentBytes = Arrays.copyOf(newBytes, newBytes.length);
67+
modified = true;
68+
}
69+
70+
@Override
71+
public void write() {
72+
if (modified) {
73+
if (originalResource != null) {
74+
originalResource.delete();
75+
}
76+
try {
77+
FileObject newResource = filer.createResource(StandardLocation.CLASS_OUTPUT,
78+
"",
79+
location,
80+
originatingElements);
81+
try (var os = newResource.openOutputStream()) {
82+
os.write(currentBytes);
83+
}
84+
} catch (Exception e) {
85+
throw new CodegenException("Failed to create resource: " + location, e);
86+
}
87+
}
88+
}
89+
}

codegen/codegen/src/main/java/io/helidon/codegen/CodegenFiler.java

+12
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ default FilerTextResource textResource(String location, Object... originatingEle
6464
throw new UnsupportedOperationException("Method textResource not implemented yet on " + getClass().getName());
6565
}
6666

67+
/**
68+
* A text resource that can be updated in the output.
69+
* Note that the resource can only be written once per processing round.
70+
*
71+
* @param location location to read/write to in the classes output directory
72+
* @param originatingElements elements that caused this file to be generated
73+
* @return the resource that can be used to update the file
74+
*/
75+
default FilerResource resource(String location, Object... originatingElements) {
76+
throw new UnsupportedOperationException("Method resource not implemented yet on " + getClass().getName());
77+
}
78+
6779
/**
6880
* Write a {@code META-INF/services} file for a specific provider interface and implementation(s).
6981
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.codegen;
18+
19+
/**
20+
* A resource from output (such as {@code target/META-INF/helidon}) that can have existing
21+
* values, and may be replaced with a new value.
22+
*/
23+
public interface FilerResource {
24+
/**
25+
* Existing bytes of the resource. Returns empty array, if the resource does not exist or is empty.
26+
*
27+
* @return bytes of the resource
28+
*/
29+
byte[] bytes();
30+
31+
/**
32+
* New bytes of the resource.
33+
*
34+
* @param newBytes new bytes to {@link #write()} to the resource file
35+
*/
36+
void bytes(byte[] newBytes);
37+
38+
/**
39+
* Writes the new bytes to the output. This operation can only be called once per codegen round.
40+
*/
41+
void write();
42+
}

integrations/graal/native-image-extension/src/main/java/io/helidon/integrations/graal/nativeimage/extension/HelidonReflectionFeature.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
import io.helidon.common.features.HelidonFeatures;
3636
import io.helidon.common.types.TypeName;
3737
import io.helidon.logging.common.LogConfig;
38-
import io.helidon.service.registry.DescriptorMetadata;
38+
import io.helidon.service.registry.DescriptorHandler;
3939
import io.helidon.service.registry.ServiceDiscovery;
4040
import io.helidon.service.registry.ServiceLoader__ServiceDescriptor;
4141
import io.helidon.service.registry.ServiceRegistryConfig;
@@ -190,7 +190,7 @@ private void processServiceDescriptors(BeforeAnalysisContext context) {
190190

191191
sd.allMetadata()
192192
.stream()
193-
.map(DescriptorMetadata::descriptor)
193+
.map(DescriptorHandler::descriptor)
194194
.filter(it -> it instanceof ServiceLoader__ServiceDescriptor)
195195
.map(it -> (ServiceLoader__ServiceDescriptor) it)
196196
.map(ServiceLoader__ServiceDescriptor::serviceType)

service/README.md

+30-5
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,39 @@ reflection (the class, and the field).
9898

9999
### Registry file format
100100

101-
The service registry uses a `service.registry` in `META-INF/helidon` directory to store the main metadata of
101+
The service registry uses a `service-registry.json` file in `META-INF/helidon` directory to store the main metadata of
102102
the service. This is to allow proper ordering of services (Service weight is one of the information stored) and
103103
lazy loading of services (which is the approach chosen in the core service registry).
104104

105-
The format is as follows:
106-
107-
```
108-
registry-type:service-descriptor-type:weight(double):contracts(comma separated)
105+
The format is as follows (using `//` to comment sections, not part of the format):
106+
107+
```json
108+
// root is an array of modules (we always generate a single module, but this allows a combined array, i.e. when using shading
109+
[
110+
{
111+
// version of the metadata file, defaults to 1 (and will always default to 1)
112+
"version": 1,
113+
// name of the module
114+
"module": "io.helidon.example",
115+
// all services in this module
116+
"services": [
117+
{
118+
// version of the service descriptor, defaults to 1 (and will always default to 1)
119+
"version": 1,
120+
// core (Service registry) or inject (Service Injection), defaults to core
121+
"type": "inject",
122+
// weight, defaults to 100
123+
"weight": 91.4,
124+
// class of the service descriptor - generated type that contains public constant INSTANCE
125+
"descriptor": "io.helidon.example.ServiceImpl__ServiceDescriptor",
126+
// all contracts this service implements
127+
"contracts": [
128+
"io.helidon.example.ServiceApi"
129+
]
130+
}
131+
]
132+
}
133+
]
109134
```
110135

111136
Example:

service/codegen/pom.xml

+8
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,13 @@
5151
<groupId>io.helidon.codegen</groupId>
5252
<artifactId>helidon-codegen-class-model</artifactId>
5353
</dependency>
54+
<dependency>
55+
<groupId>io.helidon.metadata</groupId>
56+
<artifactId>helidon-metadata-hson</artifactId>
57+
</dependency>
58+
<dependency>
59+
<groupId>io.helidon.service</groupId>
60+
<artifactId>helidon-service-metadata</artifactId>
61+
</dependency>
5462
</dependencies>
5563
</project>

0 commit comments

Comments
 (0)