Skip to content

Commit

Permalink
Merge pull request #5477 from FroMage/2299
Browse files Browse the repository at this point in the history
Fix #2299: mention service files in extension guide
  • Loading branch information
Sanne authored Nov 14, 2019
2 parents 56477a3 + 7c45ed8 commit 55d7cd0
Showing 1 changed file with 89 additions and 2 deletions.
91 changes: 89 additions & 2 deletions docs/src/main/asciidoc/writing-extensions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2025,16 +2025,103 @@ public final class MyExtProcessor {
resource.produce(new NativeImageResourceBuildItem("/security/runtime.keys")); //<1>
resource.produce(new NativeImageResourceBuildItem(
"META-INF/services/" + io.quarkus.SomeService.class.getName())); //<2>
"META-INF/my-descriptor.xml")); //<2>
resourceBundle.produce(new NativeImageResourceBuildItem("javax.xml.bind.Messages")); //<3>
}
}
----
<1> Indicate that the /security/runtime.keys classpath resource should be copied into native image.
<2> Indicate that the `io.quarkus.SomeService` ServiceLoader resource should be copied into native image.
<2> Indicate that the `META-INF/my-descriptor.xml` resource should be copied into native image
<3> Indicate that the "javax.xml.bind.Messages" resource bundle should be copied into native image.

==== Service files

If you are using `META-INF/services` files you need to register the files as resources so that your native image can find them,
but you also need to register each listed class for reflection so they can be instantiated or inspected at run-time:

[source,java]
----
public final class MyExtProcessor {
@BuildStep
void registerNativeImageReources(BuildProducer<NativeImageResourceBuildItem> resource,
BuildProducer<ReflectiveClassBuildItem> reflectionClasses) {
String service = "META-INF/services/" + io.quarkus.SomeService.class.getName();
// register the service file so it is visible in native-image
resource.produce(new NativeImageResourceBuildItem(service));
// register every listed implementation class so they can be inspected/instantiated
// in native-image at run-time
Set<String> implementations =
ServiceUtil.classNamesNamedIn(Thread.currentThread().getContextClassLoader(),
service);
reflectionClasses.produce(
new ReflectiveClassBuildItem(true, true, implementations.toArray(new String[0])));
}
}
----

This is the easiest way to get your services running natively, but it's less efficient than scanning the implementation
classes at build time and generating code that registers them at static-init time instead of relying on reflection.

You can achieve that by adapting the previous build step to use a static-init recorder instead of registering
classes for reflection:

[source,java]
----
public final class MyExtProcessor {
@BuildStep
@Record(ExecutionTime.STATIC_INIT)
void registerNativeImageReources(RecorderContext recorderContext,
SomeServiceRecorder recorder) {
String service = "META-INF/services/" + io.quarkus.SomeService.class.getName();
// read the implementation classes
Collection<Class<? extends io.quarkus.SomeService>> implementationClasses = new LinkedHashSet<>();
Set<String> implementations = ServiceUtil.classNamesNamedIn(Thread.currentThread().getContextClassLoader(),
service);
for(String implementation : implementations) {
implementationClasses.add((Class<? extends io.quarkus.SomeService>)
recorderContext.classProxy(implementation));
}
// produce a static-initializer with those classes
recorder.configure(implementationClasses);
}
}
@Recorder
public class SomeServiceRecorder {
public void configure(List<Class<? extends io.quarkus.SomeService>> implementations) {
// configure our service statically
SomeServiceProvider serviceProvider = SomeServiceProvider.instance();
SomeServiceBuilder builder = serviceProvider.getSomeServiceBuilder();
List<io.quarkus.SomeService> services = new ArrayList<>(implementations.size());
// instantiate the service implementations
for (Class<? extends io.quarkus.SomeService> implementationClass : implementations) {
try {
services.add(implementationClass.getConstructor().newInstance());
} catch (Exception e) {
throw new IllegalArgumentException("Unable to instantiate service " + implementationClass, e);
}
}
// build our service
builder.withSomeServices(implementations.toArray(new io.quarkus.SomeService[0]));
ServiceManager serviceManager = builder.build();
// register it
serviceProvider.registerServiceManager(serviceManager, Thread.currentThread().getContextClassLoader());
}
}
----


==== Object Substitution
Objects created during the build phase that are passed into the runtime need to have a default constructor in order for them to be created and configured at startup of the runtime from the build time state. If an object does not have a default constructor you will see an error similar to the following during generation of the augmented artifacts:

Expand Down

0 comments on commit 55d7cd0

Please sign in to comment.