Open dufoli opened 3 years ago
What would be the best way to collaborate on such a document? Create a new doc/PR so that we can incrementally improve it? I'd be interested in helping with this effort based on my on-going extension development experience 😄 /cc @dufoli
@metacosm ping
I propose a new documentation more dev oriented for extension instead of api oriented.
I have start to write it:
= Extension Cookbook
== Context & Objective
The purpose of this article is to be more dev oriented and less api oriented to explain how to produce extension for quarkus. So it will not detail how to generate BuildItem or use substitution, but it will explain the workflow to handle extension. First an extension is a module which will be run by quarkus during build time to generate code and inject it. It is a kind of pre-compilation.
== Organisation
There are 2 projects:
The processor is the heart of the extension all build step function is a hierarchy of dependant. Quarkus automatically detect build items input and output and run then in the right order. It mean that sometimes you add an uneeded input just ot be sure it is launched after another method.
The processor can generated bytecode, read config, and pass some data to runtime thanks to recorder (only simple structure).
== JVM : containerization
Quarkus run inside a container, and the container handle the CDI and all injected beans. So it means that processor must generate a producer of client, register servlet or route http request to backend. In order to do this stuff, there are 2 inputs:
=== Injectable client bean (config, producer)
BuildStep can generate a producer with Gizmo :
=== Register servlet
Build step can generate the ServletBuildItem which referring to the servlet class. If you can the used vert.x routes.
=== Route vertx
BuildStep must produce a RouteBuildItem and forward it to a class on Handler.
=== Removable Quarkus cleanup not used beans. So if you need to register a bean for injection but load without inject (CDI.current().select(MyClient.class).get()) There are 3 way to do it:
== Native
If you have followed previous steps, Quarkus container can manage client injection. Now it is time to work on native and that is the hard part because of all limitation you face in a native mode.
https://www.graalvm.org/reference-manual/native-image/Limitations/
=== Reflection Search full text on "Class.forName" and "loadClass" and "getMethod" on the source of module. Create a buildStep with the list of class ReflectiveClassBuildItem set methos flags to true if getMethod is used and false for other. Sometimes fields are used with reflection and constructor so paid attention to enable flags for this kind of usage.
=== ASM emit Search full text for "org.objectweb.asm" or "ClassWriter" and translate all generated class process to gizmo generation. ==> BuildStep with input BuildProducer generatedBeans
and ClassOutput classOutput = new GeneratedBeanGizmoAdaptor(generatedBeans);
=== MethodHandle MethodHandle is not supported to use substitution to replace MethodHandle with Method.invoke();
=== Proxy Search full text for "newProxyInstance" each time try to find what is the interface used for proxy and register on a processor NativeImageProxyDefinitionBuildItem.
=== Resources Get list of resources present in the jar and produce a NativeImageResourceBuildItem for each one.
=== Runtime load For this part, you have to run and if you see an error from graalvm saying that a particular class must be loaded on runtime and not static init. It comme from a static init of a source class which load a class only available on runtime (thread for example). Goal is to find the source class with the static init and not the target. First, you must add a property to the project on native profile.