Skip to content

Commit 860f3e9

Browse files
committed
Introduce @RegisterResourceBundle and @RegisterResources
1 parent 931e20c commit 860f3e9

File tree

16 files changed

+405
-1
lines changed

16 files changed

+405
-1
lines changed

.github/native-tests.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@
111111
{
112112
"category": "Misc2",
113113
"timeout": 75,
114-
"test-modules": "hibernate-validator, test-extension/tests, logging-gelf, mailer, native-config-profile, locales/all, locales/some, locales/default, jaxp, jaxb",
114+
"test-modules": "hibernate-validator, test-extension/tests, logging-gelf, mailer, native-config-profile, native-image-annotations, locales/all, locales/some, locales/default, jaxp, jaxb",
115115
"os-name": "ubuntu-latest"
116116
},
117117
{
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.quarkus.deployment.steps;
2+
3+
import org.jboss.jandex.DotName;
4+
5+
import io.quarkus.deployment.annotations.BuildProducer;
6+
import io.quarkus.deployment.annotations.BuildStep;
7+
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
8+
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem;
9+
import io.quarkus.runtime.annotations.RegisterResourceBundle;
10+
11+
public class RegisterResourceBundleBuildStep {
12+
13+
@BuildStep
14+
public void build(CombinedIndexBuildItem combinedIndexBuildItem,
15+
BuildProducer<NativeImageResourceBundleBuildItem> resourceBundle) {
16+
for (var annotationInstance : combinedIndexBuildItem.getIndex()
17+
.getAnnotations(DotName.createSimple(RegisterResourceBundle.class.getName()))) {
18+
var bundleNameValue = annotationInstance.value("bundleName");
19+
var moduleNameValue = annotationInstance.value("moduleName");
20+
if (moduleNameValue == null || moduleNameValue.asString().isEmpty()) {
21+
resourceBundle.produce(new NativeImageResourceBundleBuildItem(bundleNameValue.asString()));
22+
} else {
23+
resourceBundle.produce(new NativeImageResourceBundleBuildItem(bundleNameValue.asString(),
24+
moduleNameValue.asString()));
25+
}
26+
}
27+
}
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.quarkus.deployment.steps;
2+
3+
import org.jboss.jandex.DotName;
4+
5+
import io.quarkus.deployment.annotations.BuildProducer;
6+
import io.quarkus.deployment.annotations.BuildStep;
7+
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
8+
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourcePatternsBuildItem;
9+
import io.quarkus.runtime.annotations.RegisterResources;
10+
11+
public class RegisterResourcesBuildStep {
12+
13+
@BuildStep
14+
public void build(CombinedIndexBuildItem combinedIndexBuildItem,
15+
BuildProducer<NativeImageResourcePatternsBuildItem> resources) {
16+
for (var annotationInstance : combinedIndexBuildItem.getIndex()
17+
.getAnnotations(DotName.createSimple(RegisterResources.class.getName()))) {
18+
var builder = NativeImageResourcePatternsBuildItem.builder();
19+
var globsValue = annotationInstance.value("globs");
20+
if (globsValue != null) {
21+
builder.includeGlobs(globsValue.asStringArray());
22+
}
23+
resources.produce(builder.build());
24+
}
25+
}
26+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.quarkus.runtime.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Repeatable;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
/**
10+
* Annotation that can be used to register a resource bundle to be included in the native image.
11+
*/
12+
@Retention(RetentionPolicy.RUNTIME)
13+
@Target(ElementType.TYPE)
14+
@Repeatable(RegisterResourceBundle.List.class)
15+
public @interface RegisterResourceBundle {
16+
17+
/**
18+
* The bundle name.
19+
*/
20+
String bundleName();
21+
22+
/**
23+
* The module name (optional).
24+
*/
25+
String moduleName() default "";
26+
27+
/**
28+
* The repeatable holder for {@link RegisterResourceBundle}.
29+
*/
30+
@Retention(RetentionPolicy.RUNTIME)
31+
@Target(ElementType.TYPE)
32+
@interface List {
33+
/**
34+
* The {@link RegisterResourceBundle} instances.
35+
*
36+
* @return the instances
37+
*/
38+
RegisterResourceBundle[] value();
39+
}
40+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package io.quarkus.runtime.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Repeatable;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
/**
10+
* Annotation that can be used to register resource files to be included in the native image.
11+
*/
12+
@Retention(RetentionPolicy.RUNTIME)
13+
@Target(ElementType.TYPE)
14+
@Repeatable(RegisterResources.List.class)
15+
public @interface RegisterResources {
16+
17+
/**
18+
* Add an array of glob patterns for matching resource paths that should be added to the native image.
19+
* <p>
20+
* Use slash ({@code /}) as a path separator on all platforms. Globs must not start with slash.
21+
*/
22+
String[] globs() default {};
23+
24+
/**
25+
* The repeatable holder for {@link RegisterResources}.
26+
*/
27+
@Retention(RetentionPolicy.RUNTIME)
28+
@Target(ElementType.TYPE)
29+
@interface List {
30+
/**
31+
* The {@link RegisterResources} instances.
32+
*
33+
* @return the instances
34+
*/
35+
RegisterResources[] value();
36+
}
37+
}

docs/src/main/asciidoc/writing-native-applications-tips.adoc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,28 @@ public class MyReflectionConfiguration {
261261
Note that the order of the specified proxy interfaces is significant. For more information, see link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/Proxy.html[Proxy javadoc].
262262
====
263263

264+
=== Registering resource bundles and resource files
265+
266+
Resource bundles can be registered to be included in the native image by using `@RegisterResourceBundle`:
267+
268+
[source,java]
269+
----
270+
@RegisterResourceBundle(bundleName = "messages")
271+
@RegisterResourceBundle(bundleName = "errors", moduleName = "foo")
272+
public class NativeConfiguration {
273+
}
274+
----
275+
276+
Other resources files can be registered to be included in the native image by using `@RegisterResources`:
277+
278+
[source,java]
279+
----
280+
@RegisterResources(globs = ["path1/level*/**", "path2/level5/**"])
281+
@RegisterResources(globs = ["**.txt"])
282+
public class NativeConfiguration {
283+
}
284+
----
285+
264286
[[delay-class-init-in-your-app]]
265287
=== Delaying class initialization
266288

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>quarkus-integration-tests-parent</artifactId>
7+
<groupId>io.quarkus</groupId>
8+
<version>999-SNAPSHOT</version>
9+
<relativePath>../</relativePath>
10+
</parent>
11+
<modelVersion>4.0.0</modelVersion>
12+
13+
<artifactId>quarkus-integration-test-native-image-annotations</artifactId>
14+
<name>Quarkus - Integration Tests - Native Image Annotations</name>
15+
<description>Native Image Annotations</description>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>io.quarkus</groupId>
20+
<artifactId>quarkus-arc</artifactId>
21+
</dependency>
22+
<dependency>
23+
<groupId>io.quarkus</groupId>
24+
<artifactId>quarkus-resteasy</artifactId>
25+
</dependency>
26+
27+
<!-- test dependencies -->
28+
<dependency>
29+
<groupId>io.quarkus</groupId>
30+
<artifactId>quarkus-junit5</artifactId>
31+
<scope>test</scope>
32+
</dependency>
33+
<dependency>
34+
<groupId>io.rest-assured</groupId>
35+
<artifactId>rest-assured</artifactId>
36+
<scope>test</scope>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.assertj</groupId>
40+
<artifactId>assertj-core</artifactId>
41+
<scope>test</scope>
42+
</dependency>
43+
44+
<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
45+
<dependency>
46+
<groupId>io.quarkus</groupId>
47+
<artifactId>quarkus-arc-deployment</artifactId>
48+
<version>${project.version}</version>
49+
<type>pom</type>
50+
<scope>test</scope>
51+
<exclusions>
52+
<exclusion>
53+
<groupId>*</groupId>
54+
<artifactId>*</artifactId>
55+
</exclusion>
56+
</exclusions>
57+
</dependency>
58+
<dependency>
59+
<groupId>io.quarkus</groupId>
60+
<artifactId>quarkus-resteasy-deployment</artifactId>
61+
<version>${project.version}</version>
62+
<type>pom</type>
63+
<scope>test</scope>
64+
<exclusions>
65+
<exclusion>
66+
<groupId>*</groupId>
67+
<artifactId>*</artifactId>
68+
</exclusion>
69+
</exclusions>
70+
</dependency>
71+
</dependencies>
72+
73+
<build>
74+
<plugins>
75+
<plugin>
76+
<groupId>io.quarkus</groupId>
77+
<artifactId>quarkus-maven-plugin</artifactId>
78+
<executions>
79+
<execution>
80+
<goals>
81+
<goal>build</goal>
82+
</goals>
83+
<configuration>
84+
<skip>false</skip>
85+
</configuration>
86+
</execution>
87+
</executions>
88+
</plugin>
89+
<plugin>
90+
<groupId>org.apache.maven.plugins</groupId>
91+
<artifactId>maven-surefire-plugin</artifactId>
92+
<executions>
93+
<execution>
94+
<id>default-test</id>
95+
<phase/>
96+
</execution>
97+
</executions>
98+
</plugin>
99+
</plugins>
100+
</build>
101+
102+
<profiles>
103+
<profile>
104+
<id>native-image</id>
105+
<activation>
106+
<property>
107+
<name>native</name>
108+
</property>
109+
</activation>
110+
<properties>
111+
<quarkus.native.enabled>true</quarkus.native.enabled>
112+
</properties>
113+
<build>
114+
<plugins>
115+
<plugin>
116+
<groupId>org.apache.maven.plugins</groupId>
117+
<artifactId>maven-surefire-plugin</artifactId>
118+
<configuration>
119+
<skipTests>${native.surefire.skip}</skipTests>
120+
</configuration>
121+
</plugin>
122+
<plugin>
123+
<groupId>org.apache.maven.plugins</groupId>
124+
<artifactId>maven-failsafe-plugin</artifactId>
125+
<executions>
126+
<execution>
127+
<goals>
128+
<goal>integration-test</goal>
129+
<goal>verify</goal>
130+
</goals>
131+
<configuration>
132+
<systemPropertyVariables>
133+
<native.image.path>
134+
${project.build.directory}/${project.build.finalName}-runner
135+
</native.image.path>
136+
</systemPropertyVariables>
137+
</configuration>
138+
</execution>
139+
</executions>
140+
</plugin>
141+
</plugins>
142+
</build>
143+
</profile>
144+
145+
</profiles>
146+
147+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package io.quarkus.it.nat.annotation;
2+
3+
import java.io.IOException;
4+
import java.nio.charset.StandardCharsets;
5+
import java.util.Locale;
6+
import java.util.ResourceBundle;
7+
8+
import jakarta.ws.rs.GET;
9+
import jakarta.ws.rs.Path;
10+
import jakarta.ws.rs.Produces;
11+
12+
@Path("/native-image-annotations")
13+
public class NativeImageAnnotationsResource {
14+
15+
@Path("/access-classpath-resource")
16+
@Produces("text/plain")
17+
@GET
18+
public String accessClasspathResource() {
19+
try (var resourceInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")) {
20+
if (resourceInputStream == null)
21+
return "Resource not found";
22+
return new String(resourceInputStream.readAllBytes(), StandardCharsets.UTF_8);
23+
} catch (IOException e) {
24+
throw new RuntimeException(e);
25+
}
26+
}
27+
28+
@Path("/access-resource-bundle/en")
29+
@Produces("text/plain")
30+
@GET
31+
public String accessResourceBundleEn() {
32+
var resourceBundle = ResourceBundle.getBundle("messages", Locale.ENGLISH);
33+
if (resourceBundle == null)
34+
return "Resource bundle not found";
35+
return resourceBundle.getString("message.sayHello");
36+
}
37+
38+
@Path("/access-resource-bundle/ar")
39+
@Produces("text/plain")
40+
@GET
41+
public String accessResourceBundleAr() {
42+
var resourceBundle = ResourceBundle.getBundle("messages", Locale.forLanguageTag("ar"));
43+
if (resourceBundle == null)
44+
return "Resource bundle not found";
45+
return resourceBundle.getString("message.sayHello");
46+
}
47+
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package io.quarkus.it.nat.annotation;
2+
3+
import io.quarkus.runtime.annotations.RegisterResourceBundle;
4+
import io.quarkus.runtime.annotations.RegisterResources;
5+
6+
@RegisterResources(globs = "file.txt")
7+
@RegisterResourceBundle(bundleName = "messages")
8+
public class NativeImageHints {
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Args=-H:IncludeLocales=en,ar

0 commit comments

Comments
 (0)