Fusion Stack
Fusion framework aims at providing a very light framework.
Core Values
For cloud applications, being the most reactive possible is a key criteria so Fusion chose to:
-
Be build time oriented: only delegate to runtime the bean resolution (to enable dynamic module aggregation) and not the bean and model discovery nor proxy generation,
-
Stay flexible: even if the model is generated at build time you can generally still customize it by removing a bean and adding your own one,
-
Be native friendly: some applications need to be native to start very fast and bypass the classloading and a bunch of JVM init, for that purpose we ensure the framework is GraalVM friendly - using Apache Geronimo Arthur or not.
Features
-
Field/constructor injections
-
Contexts/scopes
-
Default scope (
@DefaultScoped
) is to create an instance per lookup/injection -
Application scope (
@ApplicationScoped
) creates an instance per container and instantiates it lazily (at first call)
-
-
Event bus
-
Start
/Stop
events are fired with container related lifecycle hooks,
-
-
Lifecycle:
@Init
/@Destroy
to react to the bean lifecycle, -
Optional<MyClass>
injections, -
Basic cloud friendly configuration ,
-
Adding
fusion-json
module, you can get JSON records mapping support without reflection, -
Adding
fusion-http-server
module, you get an Apache Tomcat abstraction enabling to write any HTTP endpoint in an efficient and GraalVM friendly manner, -
Adding `fusion-testing` module, you get some JUnit 5 integration enabling to test easily your code.
-
Adding `fusion-cli` module, you can generate simple CLI applications just writing commands.
TIP
|
you can find examples on the examples page. |
No interceptor support
Since some years we got used to see declarative interceptors (annotations) like in this snippet:
public class MyBean {
@Traced
public void doSomething() {
// ...
}
}
These are great and the container is actually linking the annotation to an implementation (a bean in general) which intercepts the call. This is not bad but has some design pitfalls:
-
Most interceptors will use parameters and for such a generic approach to work, it needs an
Object[]
(orList
) of parameters. This is really not fast (it requires to allocate an array for that purpose). -
It requires to know and understand the rules between class interceptors, method interceptors, appending/overriding when relevant plus the same with parent classes. All that can quickly become complex.
-
It is often static: once put on a method disabling an interceptor requires the underlying library to be able to do that or to use some advanced customization at startup to do it.
For these reasons, we think that we don't need an interceptor solution in Fusion framework but we don't say the underlying feature is pointless, not at all. However, thanks to a more modern programming style, we can use a more functional approach to solve the same problem. Therefore, previous example would rather become:
public class MyBean {
public void doSomething() {
tracing(() -> {
// ...
});
}
}
The big advantage is you can use some static utility if you want but also rely on beans and even combine more efficiently interceptions in a custom and configurable fashion:
public class MyBean {
public void doSomething() {
tracing(() -> timed(() -> logged(() -> {
// ...
})));
}
}
can become:
public class MyBean {
@Injection
MyObservabilityService obs;
public void doSomething() {
obs.instrumented(() -> {
// ...
});
}
}
If you compare the case with parameters it is way more efficient in general since you just do a standard parameter passing call:
public class MyBean {
@Injection
EntityManager em; // assume the application used JPA - not required
@Injection
JpaService jpa; // custom bean to handle transactions for ex
public void store(final Transaction tx) {
tracing(
// no Object[] created for an interceptor
// and no reflection to extract the id
tx.id(),
() -> jpa.tx(() -> em.persist(tx)));
}
}
TIP
|
going with this solution can, however, get the chaining lambda pitfall (a.k.a. callback hell in JavaScript), to solve this one we encourage you to ensure your "interceptor" can be chained properly using the same kind of callback. Here is an example (the important part is more the signature than the fact it is a
Thanks this definition which commonly agreed to use
If you want to go further you can use a
This is what the class |
Last tip: you interceptor can work with CompletionStage
to add some behavior before/after the call even if the result is not computed synchronously ;).
Limitations
NOTE
|
these are limitations as of today, none are technically strong limitations we can't fix at a later point if desired. |
-
A no-arg constructor must be available for any class bean,
-
If a method producer bean is
AutoCloseable
then it will be automatically closed, -
Event methods can not be package scope if the enclosing bean uses a subclass proxy (like
@ApplicationScoped
context), -
Constructor injections are supported but for proxied scopes (
@ApplicationScoped
for ex) it requires a default no-arg constructor (in scopeprotected
orpublic
) in the class (if not existing the instantiation constructor will be called with null parameters), -
Event bus listeners can only have the event as method parameter,
-
Only classes are supported exception for method producers which can return a
ParameterizedType
(ex:List<String>
) but injections must exactly match this type andList
/Set
injections are handled by looking up all beans matching the parameter.
Setup
See setup page to see how to get your project started.