This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new c35950687a97 CAMEL-16861: Update docs
c35950687a97 is described below
commit c35950687a97e07e8a04d3f373303907e641f4e8
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Feb 16 16:13:38 2026 +0100
CAMEL-16861: Update docs
---
.../src/main/docs/modules/eips/pages/saga-eip.adoc | 569 +++++++++++++++++++--
.../src/main/resources/templates/code-java.tmpl | 3 +
.../apache/camel/main/stub/StubBeanRepository.java | 27 +-
3 files changed, 543 insertions(+), 56 deletions(-)
diff --git
a/core/camel-core-engine/src/main/docs/modules/eips/pages/saga-eip.adoc
b/core/camel-core-engine/src/main/docs/modules/eips/pages/saga-eip.adoc
index 026d166d10ba..0e67b73e965e 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/saga-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/saga-eip.adoc
@@ -27,7 +27,7 @@ The Saga EIP implementation based on the MicroProfile sandbox
spec is indeed cal
It also supports coordination of external *heterogeneous services*,
written with any language/technology and also running outside a JVM.
-NOTE: see camel-lra.
+NOTE: See also xref:components:others:lra.adoc[Camel LRA]
Sagas don't use locks on data.
Instead, they define the concept of "Compensating Action"
@@ -181,6 +181,11 @@ one to create the order and one to take the credit.
*Both actions must be executed, or none of them*:
an order placed without enough credits can be considered an inconsistent
outcome (and a payment without an order).
+[tabs]
+====
+
+Java::
++
[source,java]
----
from("direct:buy")
@@ -189,6 +194,36 @@ from("direct:buy")
.to("direct:reserveCredit");
----
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:buy"/>
+ <saga>
+ <to uri="direct:newOrder"/>
+ <to uri="direct:reserveCredit"/>
+ </saga>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:buy
+ steps:
+ - saga:
+ steps:
+ - to:
+ uri: direct:newOrder
+ - to:
+ uri: direct:reserveCredit
+----
+====
+
*That's it*. The buy action will not change for the rest of the examples.
We'll just see different options that can be used to model the "New Order" and
"Reserve Credit" actions in the following.
[NOTE]
@@ -199,6 +234,11 @@ We could have used *http* or other kinds of endpoint with
the LRA Saga service.
Both services called by the `direct:buy` route can *participate in the Saga*
and declare their compensating actions.
+[tabs]
+====
+
+Java::
++
[source,java]
----
from("direct:newOrder")
@@ -206,10 +246,52 @@ from("direct:newOrder")
.propagation(SagaPropagation.MANDATORY)
.compensation("direct:cancelOrder")
.transform().header(Exchange.SAGA_LONG_RUNNING_ACTION)
- .bean(orderManagerService, "newOrder")
+ .bean("orderManagerService", "newOrder")
.log("Order ${body} created");
----
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:newOrder"/>
+ <saga propagation="MANDATORY">
+ <compensation uri="direct:cancelOrder"/>
+ <transform>
+ <header>Long-Running-Action</header>
+ </transform>
+ <bean ref="orderManagerService" method="newOrder"/>
+ <log message="Order ${body} created"/>
+ </saga>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:newOrder
+ steps:
+ - saga:
+ propagation: MANDATORY
+ compensation:
+ uri: direct:cancelOrder
+ steps:
+ - transform:
+ expression:
+ header:
+ expression: Long-Running-Action
+ - bean:
+ ref: orderManagerService
+ method: newOrder
+ - log:
+ message: "Order ${body} created"
+----
+====
+
Here the propagation mode is set to `MANDATORY` meaning that any exchange
flowing in this route must be already part of a saga.
And it is the case in this example, since the saga is created in the
`direct:buy` route.
@@ -222,14 +304,53 @@ but it is not a requirement (options can be used as an
alternative solution).
The compensating action of `direct:newOrder` is `direct:cancelOrder`, and it's
shown below:
+[tabs]
+====
+
+Java::
++
[source,java]
----
from("direct:cancelOrder")
.transform().header(Exchange.SAGA_LONG_RUNNING_ACTION)
- .bean(orderManagerService, "cancelOrder")
+ .bean("orderManagerService", "cancelOrder")
.log("Order ${body} cancelled");
----
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:cancelOrder"/>
+ <transform>
+ <header>Long-Running-Action</header>
+ </transform>
+ <bean ref="orderManagerService" method="cancelOrder"/>
+ <log message="Order ${body} cancelled"/>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:cancelOrder
+ steps:
+ - transform:
+ expression:
+ header:
+ expression: Long-Running-Action
+ - bean:
+ ref: orderManagerService
+ method: cancelOrder
+ - log:
+ message: "Order ${body} cancelled"
+----
+====
+
It is called automatically by the Saga EIP implementation when the order
should be canceled.
It should not terminate with error.
@@ -256,6 +377,11 @@ this approach may work in many contexts, but it's
*heuristic*.
The credit service may be implemented almost in the same way as the order
service.
+[tabs]
+====
+
+Java::
++
[source,java]
----
// action
@@ -264,16 +390,81 @@ from("direct:reserveCredit")
.propagation(SagaPropagation.MANDATORY)
.compensation("direct:refundCredit")
.transform().header(Exchange.SAGA_LONG_RUNNING_ACTION)
- .bean(creditService, "reserveCredit")
+ .bean("creditService", "reserveCredit")
.log("Credit ${header.amount} reserved in action ${body}");
// compensation
from("direct:refundCredit")
.transform().header(Exchange.SAGA_LONG_RUNNING_ACTION)
- .bean(creditService, "refundCredit")
+ .bean("creditService", "refundCredit")
.log("Credit for action ${body} refunded");
----
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:reserveCredit"/>
+ <saga propagation="MANDATORY">
+ <compensation uri="direct:refundCredit"/>
+ <transform>
+ <header>Long-Running-Action</header>
+ </transform>
+ <bean ref="creditService" method="reserveCredit"/>
+ <log message="Credit ${header.amount} reserved in action ${body}"/>
+ </saga>
+</route>
+
+<route>
+ <from uri="direct:refundCredit"/>
+ <transform>
+ <header>Long-Running-Action</header>
+ </transform>
+ <bean ref="creditService" method="refundCredit"/>
+ <log message="Credit for action ${body} refunded"/>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:reserveCredit
+ steps:
+ - saga:
+ propagation: MANDATORY
+ compensation:
+ uri: direct:refundCredit
+ steps:
+ - transform:
+ expression:
+ header:
+ expression: Long-Running-Action
+ - bean:
+ ref: creditService
+ method: reserveCredit
+ - log:
+ message: "Credit ${header.amount} reserved in action ${body}"
+- route:
+ from:
+ uri: direct:refundCredit
+ steps:
+ - transform:
+ expression:
+ header:
+ expression: Long-Running-Action
+ - bean:
+ ref: creditService
+ method: refundCredit
+ - log:
+ message: "Credit for action ${body} refunded"
+----
+====
+
+
Here the compensating action for a credit reservation is a refund.
This completes the example. It can be run with both implementations of the
Saga EIP, as it does not involve remote endpoints.
@@ -291,6 +482,13 @@ We will not want to start to prepare the order if the
payment is not done (unlik
This can be done easily with a modified version of the `direct:newOrder`
endpoint:
+[tabs]
+====
+
+Java::
++
+The compensation route is the same as in the previous example above
++
[source,java]
----
from("direct:newOrder")
@@ -299,7 +497,7 @@ from("direct:newOrder")
.compensation("direct:cancelOrder")
.completion("direct:completeOrder") // completion endpoint
.transform().header(Exchange.SAGA_LONG_RUNNING_ACTION)
- .bean(orderManagerService, "newOrder")
+ .bean("orderManagerService", "newOrder")
.log("Order ${body} created");
// direct:cancelOrder is the same as in the previous example
@@ -307,18 +505,92 @@ from("direct:newOrder")
// called on successful completion
from("direct:completeOrder")
.transform().header(Exchange.SAGA_LONG_RUNNING_ACTION)
- .bean(orderManagerService, "findExternalId")
+ .bean("orderManagerService", "findExternalId")
.to("jms:prepareOrder")
.log("Order ${body} sent for preparation");
----
+XML::
++
+The compensation route is the same as in the previous example above
++
+[source,xml]
+----
+<route>
+ <from uri="direct:newOrder"/>
+ <saga propagation="MANDATORY">
+ <completion uri="direct:completeOrder"/>
+ <compensation uri="direct:cancelOrder"/>
+ <transform>
+ <header>Long-Running-Action</header>
+ </transform>
+ <bean ref="orderManagerService" method="newOrder"/>
+ <log message="Order ${body} created"/>
+ </saga>
+</route>
+
+<route>
+ <from uri="direct:completeOrder"/>
+ <transform>
+ <header>Long-Running-Action</header>
+ </transform>
+ <bean ref="orderManagerService" method="findExternalId"/>
+ <to uri="jms:prepareOrder"/>
+ <log message="Order ${body} sent for preparation"/>
+</route>
+----
+
+YAML::
++
+The compensation route is the same as in the previous example above
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:newOrder
+ steps:
+ - saga:
+ propagation: MANDATORY
+ compensation:
+ uri: direct:cancelOrder
+ completion:
+ uri: direct:completeOrder
+ steps:
+ - transform:
+ expression:
+ header:
+ expression: Long-Running-Action
+ - bean:
+ ref: orderManagerService
+ method: newOrder
+ - log:
+ message: "Order ${body} created"
+- route:
+ from:
+ uri: direct:completeOrder
+ steps:
+ - transform:
+ expression:
+ header:
+ expression: Long-Running-Action
+ - bean:
+ ref: orderManagerService
+ method: findExternalId
+ - to:
+ uri: jms:prepareOrder
+ - log:
+ message: "Order ${body} sent for preparation"
+----
+====
+
When the Saga is completed, the order is sent to a JMS queue for preparation.
Like compensating actions, also completion actions may be called multiple
times by the Saga coordinator.
Especially in case of errors, like network errors.
In this example, the service listening to the `prepareOrder` JMS queue should
be prepared to hold possible duplicates.
-NOTE: Check the Idempotent Consumer EIP for examples on how to handle
duplicates.
+TIP: Check the xref:idempotentConsumer-eip.adoc[Idempotent Consumer EIP] for
examples on how to handle duplicates.
=== Using Custom Identifiers and Options
@@ -328,12 +600,19 @@ This is not always a desired approach, as it may pollute
the business logic and
An alternative approach is to use Saga options to "register" custom
identifiers.
For example, the credit service may be refactored as follows:
+[tabs]
+====
+
+Java::
++
+Notice the listing below is not using the `Exchange.SAGA_LONG_RUNNING_ACTION`
header at all.
++
[source,java]
----
// action
from("direct:reserveCredit")
- .bean(idService, "generateCustomId") // generate a custom ID and set it in
the body
- .to("direct:creditReservation")
+ .bean("idService", "generateCustomId") // generate a custom ID and set it in
the body
+ .to("direct:creditReservation");
// delegate action
from("direct:creditReservation")
@@ -341,21 +620,104 @@ from("direct:creditReservation")
.propagation(SagaPropagation.SUPPORTS)
.option("CreditId", body()) // mark the current body as needed in the
compensating action
.compensation("direct:creditRefund")
- .bean(creditService, "reserveCredit")
+ .bean("creditService", "reserveCredit")
.log("Credit ${header.amount} reserved. Custom Id used is ${body}");
// called only if the saga is canceled
from("direct:creditRefund")
.transform(header("CreditId")) // retrieve the CreditId option from headers
- .bean(creditService, "refundCredit")
+ .bean("creditService", "refundCredit")
.log("Credit for Custom Id ${body} refunded");
----
-*Note how the previous listing is not using the
`Exchange.SAGA_LONG_RUNNING_ACTION` header at all.*
+XML::
++
+Notice the listing below is not using the `Long-Running-Action` header at all.
++
+[source,xml]
+----
+<route>
+ <from uri="direct:reserveCredit"/>
+ <bean ref="idService" method="generateCustomId"/>
+ <to uri="direct:creditReservation"/>
+</route>
+
+<route>
+ <from uri="direct:creditReservation"/>
+ <saga propagation="SUPPORTS">
+ <option key="CreditId">
+ <simple>${body}</simple>
+ </option>
+ <compensation uri="direct:creditRefund"/>
+ <bean ref="creditService" method="reserveCredit"/>
+ <log message="Credit ${header.amount} reserved. Custom Id used is
${body}"/>
+ </saga>
+</route>
+
+<route>
+ <from uri="direct:creditRefund"/>
+ <transform>
+ <header>CreditId</header>
+ </transform>
+ <bean ref="creditService" method="refundCredit"/>
+ <log message="Credit for Custom Id ${body} refunded"/>
+</route>
+----
+
+YAML::
++
+Notice the listing below is not using the `Long-Running-Action` header at all.
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:reserveCredit
+ steps:
+ - bean:
+ ref: idService
+ method: generateCustomId
+ - to:
+ uri: direct:creditReservation
+- route:
+ from:
+ uri: direct:creditReservation
+ steps:
+ - saga:
+ propagation: SUPPORTS
+ option:
+ key: CreditId
+ expression:
+ simple:
+ expression: "${body}"
+ compensation:
+ uri: direct:creditRefund
+ steps:
+ - bean:
+ ref: creditService
+ method: reserveCredit
+ - log:
+ message: "Credit ${header.amount} reserved. Custom Id used
is ${body}"
+- route:
+ from:
+ uri: direct:creditRefund
+ steps:
+ - transform:
+ expression:
+ header:
+ expression: CreditId
+ - bean:
+ ref: creditService
+ method: refundCredit
+ - log:
+ message: "Credit for Custom Id ${body} refunded"
+----
+====
+
Since the `direct:creditReservation` endpoint can be now called also from
outside a Saga, the propagation mode can be set to `SUPPORTS`.
-NOTE: Multiple options* can be declared in a Saga route.
+TIP: Multiple `option`(s) can be declared in a Saga route.
=== Setting Timeouts
@@ -371,6 +733,11 @@ When the timeout expires, the Saga EIP will decide to
*cancel the Saga* (and com
Timeouts can be set on Saga participants as follows:
+[tabs]
+====
+
+Java::
++
[source,java]
----
from("direct:newOrder")
@@ -383,6 +750,41 @@ from("direct:newOrder")
.log("Order ${body} created");
----
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:newOrder"/>
+ <saga propagation="MANDATORY" timeout="1m">
+ <completion uri="direct:completeOrder"/>
+ <compensation uri="direct:cancelOrder"/>
+ <log message="Order ${body} created"/>
+ </saga>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:newOrder
+ steps:
+ - saga:
+ propagation: MANDATORY
+ timeout: 1m
+ compensation:
+ uri: direct:cancelOrder
+ completion:
+ uri: direct:completeOrder
+ steps:
+ - log:
+ message: "Order ${body} created"
+----
+====
+
All participants, e.g., credit service, order service, can set their own
timeout.
When a participant joins an existing transaction, the timeout of the already
active saga can be influenced.
It should calculate the moment in time
@@ -391,8 +793,13 @@ When this moment is earlier than the moment calculated for
the saga at that time
this new moment becomes the timeout moment for the saga.
So when multiple participants define a timeout period, the earliest one will
trigger the cancellation of the saga.
-A timeout can also be specified at saga level as follows:
+A timeout can also be specified at Saga level as follows:
+[tabs]
+====
+
+Java::
++
[source,java]
----
from("direct:buy")
@@ -402,6 +809,38 @@ from("direct:buy")
.to("direct:reserveCredit");
----
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:buy"/>
+ <saga timeout="5m">
+ <to uri="direct:newOrder"/>
+ <to uri="direct:reserveCredit"/>
+ </saga>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:buy
+ steps:
+ - saga:
+ timeout: 5m
+ steps:
+ - to:
+ uri: direct:newOrder
+ - to:
+ uri: direct:reserveCredit
+----
+====
+
+
=== Choosing Propagation
In the examples above, we have used the `MANDATORY` and `SUPPORTS` propagation
modes, but also the `REQUIRED` propagation mode,
@@ -428,6 +867,11 @@ That is because the saga is not completed when the
exchange that creates it is d
This is often the case for Sagas that have long execution times (hours, days).
In these cases, the `MANUAL` completion mode should be used.
+[tabs]
+====
+
+Java::
++
[source,java]
----
from("direct:mysaga")
@@ -444,15 +888,77 @@ from("direct:mysaga")
from("seda:operationCompleted") // an asynchronous callback
.saga()
.propagation(SagaPropagation.MANDATORY)
- .bean(controlService, "actionExecuted")
- .choice()
- .when(body().isEqualTo("ok"))
+ .bean("controlService", "actionExecuted")
+ .filter(simple("${body} == 'ok'"))
.to("saga:complete") // complete the current saga manually (saga
component)
- .end()
+ .end();
// You can put here the direct:finalize endpoint to execute final actions
----
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:mysaga"/>
+ <saga completionMode="MANUAL" timeout="2h0m0s">
+ <completion uri="direct:finalize"/>
+ <to uri="seda:newOrder"/>
+ <to uri="seda:reserveCredit"/>
+ </saga>
+</route>
+
+<route>
+ <from uri="seda:operationCompleted"/>
+ <saga propagation="MANDATORY">
+ <bean ref="controlService" method="actionExecuted"/>
+ <filter>
+ <simple>${body} == 'ok'</simple>
+ <to uri="saga:complete"/>
+ </filter>
+ </saga>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:mysaga
+ steps:
+ - saga:
+ completionMode: MANUAL
+ timeout: 2h
+ completion:
+ uri: direct:finalize
+ steps:
+ - to:
+ uri: seda:newOrder
+ - to:
+ uri: seda:reserveCredit
+- route:
+ from:
+ uri: seda:operationCompleted
+ steps:
+ - saga:
+ propagation: MANDATORY
+ steps:
+ - bean:
+ ref: controlService
+ method: actionExecuted
+ - filter:
+ expression:
+ simple:
+ expression: "${body} == 'ok'"
+ steps:
+ - to:
+ uri: saga:complete
+----
+====
+
Setting the completion mode to `MANUAL` means that the saga is not completed
when the exchange is processed in the route `direct:mysaga` but
it will last longer (max duration is set to 2 hours).
@@ -466,28 +972,3 @@ But Sagas add a lot of value,
since they guarantee that even in the presence of unexpected issues (servers
crashing, messages are lost, etc.)
there will always be a consistent outcome: order placed and credit reserved,
or none of them changed.
In particular, if the Saga is not completed within 2 hours, the compensation
mechanism will take care of fixing the status.
-
-== Using Saga with XML DSL
-
-Saga features are also available for users that want to use the XML DSL.
-
-The following snippet shows an example:
-
-[source,xml]
-----
-<route>
- <from uri="direct:start"/>
- <saga>
- <compensation uri="direct:compensation" />
- <completion uri="direct:completion" />
- <option optionName="myOptionKey">
- <constant>myOptionValue</constant>
- </option>
- <option optionName="myOptionKey2">
- <constant>myOptionValue2</constant>
- </option>
- </saga>
- <to uri="direct:action1" />
- <to uri="direct:action2" />
-</route>
-----
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/code-java.tmpl
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/code-java.tmpl
index 603f724e54c2..a2427fdcf288 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/code-java.tmpl
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/code-java.tmpl
@@ -1,3 +1,6 @@
+import java.util.*;
+import java.util.concurrent.*;
+
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
diff --git
a/dsl/camel-kamelet-main-support/src/main/java/org/apache/camel/main/stub/StubBeanRepository.java
b/dsl/camel-kamelet-main-support/src/main/java/org/apache/camel/main/stub/StubBeanRepository.java
index 85cabf89d5fd..d60ca6cde734 100644
---
a/dsl/camel-kamelet-main-support/src/main/java/org/apache/camel/main/stub/StubBeanRepository.java
+++
b/dsl/camel-kamelet-main-support/src/main/java/org/apache/camel/main/stub/StubBeanRepository.java
@@ -31,8 +31,6 @@ import
org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy;
import org.apache.camel.processor.aggregate.MemoryAggregationRepository;
import org.apache.camel.processor.loadbalancer.LoadBalancer;
import org.apache.camel.processor.loadbalancer.RoundRobinLoadBalancer;
-import org.apache.camel.saga.CamelSagaService;
-import org.apache.camel.saga.InMemorySagaService;
import org.apache.camel.spi.AggregationRepository;
import org.apache.camel.spi.BeanRepository;
import org.apache.camel.spi.ClaimCheckRepository;
@@ -48,6 +46,14 @@ public class StubBeanRepository implements BeanRepository {
private final static Logger LOG =
LoggerFactory.getLogger(StubBeanRepository.class);
+ private final IdempotentRepository service1 = new
MemoryIdempotentRepository();
+ private final AggregationRepository service2 = new
MemoryAggregationRepository();
+ private final ClaimCheckRepository service3 = new
DefaultClaimCheckRepository();
+ private final StateRepository<?, ?> service4 = new MemoryStateRepository();
+ private final AggregationStrategy service5 = new
GroupedBodyAggregationStrategy();
+ private final AggregateController service6 = new
DefaultAggregateController();
+ private final LoadBalancer service7 = new RoundRobinLoadBalancer();
+
private final String stubPattern;
public StubBeanRepository(String stubPattern) {
@@ -95,28 +101,25 @@ public class StubBeanRepository implements BeanRepository {
// add repositories and other stuff we need to stub out, so they run
noop/in-memory only
// and do not start live connections to databases or other services
if (IdempotentRepository.class.isAssignableFrom(type)) {
- return (T) new MemoryIdempotentRepository();
+ return (T) service1;
}
if (AggregationRepository.class.isAssignableFrom(type)) {
- return (T) new MemoryAggregationRepository();
+ return (T) service2;
}
if (ClaimCheckRepository.class.isAssignableFrom(type)) {
- return (T) new DefaultClaimCheckRepository();
+ return (T) service3;
}
if (StateRepository.class.isAssignableFrom(type)) {
- return (T) new MemoryStateRepository();
+ return (T) service4;
}
if (AggregationStrategy.class.isAssignableFrom(type)) {
- return (T) new GroupedBodyAggregationStrategy();
+ return (T) service5;
}
if (AggregateController.class.isAssignableFrom(type)) {
- return (T) new DefaultAggregateController();
- }
- if (CamelSagaService.class.isAssignableFrom(type)) {
- return (T) new InMemorySagaService();
+ return (T) service6;
}
if (LoadBalancer.class.isAssignableFrom(type)) {
- return (T) new RoundRobinLoadBalancer();
+ return (T) service7;
}
if (Logger.class.isAssignableFrom(type)) {
return (T) LOG;