Managing object construction in Quarkus CDI - cdi

I have a custom Logger that internally uses the jboss logger:
#Dependent
class LogImpl implements Log { //LogImpl & Log are custom
#Inject
Logger sink; // this is jboss logger
...
}
I inject Log wherever it is required:
class MyService {
#Inject
Log log;
}
Calls to log.debug(...), log.info(...) etc. on the log prints LogImpl as the logger name/category in the logs. What I need logged as the logger name/category is the name of the class where #Log is injected. In the example about, I need the logger name to be printed as MyService and not LogImpl.

Figured it out. Used a Producer to instantiate the custom log. InjectionPoint is used to get the class name of the object where Log in #Injected .
#ApplicationScoped
public class LogProducer {
#Produces
#Dependent
Log createLog(InjectionPoint injectionPoint) {
return new LogImpl(injectionPoint.getMember().getDeclaringClass().getCanonicalName());
}
}
class LogImpl implements Log {
private final Logger sink;
public LogImpl(String name) {
this.sink = Logger.getLogger(name); // Manual instantiation instead of #Inject. The producer calls this constructor with the correct log/category name.
}
...
}

Related

Quarkus configurable beans with autoinjecting

is it possible to have the same class as multiple beans with different configurations. That still get auto injection? I tried following:
class Config1 {
#Scope1
#Produces
#ApplicationScoped
public Parser parser1() {
return new MySpecialParser("key1");
}
}
class Config2 {
#Scope2
#Produces
#ApplicationScoped
public Parser parser2() {
return new MySpecialParser("key2");
}
}
class MySpecialParser extends Parser {
// ...
}
abstract class Parser {
#Inject
#Localized("en")
Messages messages;
Parser(String config) {
// ...
}
}
Something like this. But when I inject the parser the messages are always null.
In Spring exists AutowireCapableBeanFactory for use case like that. But maybe my initial thought is just wrong and in Quarkus I should do it differently.
Has someone a clue?
This is not possible in Quarkus. If you create an instance manually, which you do in the producer methods, no dependency injection is performed on it and you have to fully construct the instance yourself.
You may want to inject some dependencies into the producer methods, which is possible, as each producer method parameter is actually an injection point:
class Config1 {
#Scope1
#Produces
#ApplicationScoped
public Parser parser1(#Localized("en") Messages messages) {
return new MySpecialParser("key1", messages);
}
}
(There are facilities in full CDI that let you do something similar, but they are not compatible with the Quarkus build-time oriented architecture and Quarkus doesn't implement them.)

How to run the SpringBootTest with only a single bean and with included resilience4j annotations

I would like to run an integration test of a single bean with resilience4j annotated method in a spring boot app. My intent is to test resiliency of bean method calls while not loading the full spring context.
The setup is as follows:
Dependencies include the following:
io.github.resilience4j:resilience4j-spring-boot2
io.github.resilience4j:resilience4j-reactor
org.springframework.boot:spring-boot-starter-aop
The resilience4j time limited spring bean with method to test:
#Service
public class FooService {
#TimeLimiter(name = "fooTimeLimiter")
public FooResponse foo() {
//entertain operation that might timeout
}
}
Configuration:
resilience4j.timelimiter.instances.fooTimeLimiter.timeoutDuration=1s
And the test:
#SpringBootTest
#ContextConfiguration(classes = FooService.class)
public class FooServiceIT {
#Autowired
private FooService service;
#MockBean
private Bar bar;
#Test
void foo_timeout() {
//setup mocks so the operation delays the output and shall end up with timeout
Assertions.assertThrows(TimeoutException.class, () -> service.foo());
}
}
However, the TimeLimiterAdvice.proceed() is not entertained, no timeout exception is thrown and the test fails.
Same question has been asked here: Testing SpringBoot with annotation-style Resilience4j but there is no solution.
I tried both approaches - implement FooService interface and program directly using the concrete class. With the same result.
How can I achieve the time limiter annotation is taken into account in my test?
Edit: I even tried plain spring test (no spring boot) with the following setup:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class FooServiceIT {
#Configuration
#Import({TimeLimiterConfiguration.class, FallbackConfiguration.class, SpelResolverConfiguration.class})
static class ContextConfiguration {
#Bean
public FooService fooService() {
//prepare bean;
}
#Bean
public TimeLimiterConfigurationProperties timeLimiterConfigurationProperties() {
return new TimeLimiterConfigurationProperties();
}
}
#Autowired
private FooService service;
//tests...
}
Same result (i.e. no timeout exception).
When dealing with SpringBootTest and #CircuitBreaker, it was sufficient to add #EnableAspectJAutoProxy annotation to the test. After this change, the CircuitBreakerAspect was entertained and the test behaves as expected.
In order to make #TimeLimiter working as expected, one need to add #Bulkhead annotation to the method as well.
The updated method looks as follows:
#Bulkhead(name = "fooBulkhead", type = Type.THREADPOOL)
#CircuitBreaker(
name = "fooCircuitBreaker",
fallbackMethod = "fooFallback"
)
#TimeLimiter(
name = "fooTimeLimiter"
)
public CompletableFuture<FooResponse> foo() {
//...
}
and the test:
#SpringBootTest(classes = FooService.class)
#EnableAspectJAutoProxy
#Import(value = {CircuitBreakerAutoConfiguration.class, TimeLimiterAutoConfiguration.class, BulkheadAutoConfiguration.class})
public class FooServiceIT {
//...
}

Micronaut: Unable to inject a bean inside Quartz job

I am trying to inject bean (Either data access GORM service or any other bean) in the Quartz Job implemented class, but it always shows null. Same beans (GORM or other beans) are able to inject without any issues in other classes.
can you please help me to retrieve any bean in this Job class.
My Quartz job
#Singleton
#Slf4j
class MyQuartzJob implements Job {
#Inject
MyHttpBean myHttpBean // unable to inject
#Inject
ApplicationContext appContext // unable to inject
#Inject
MyGORMService myGormService // unable to inject
}
#Singleton
#Slf4j
class MyHttpBean {
// business logic
}
code to invoke QuartzJob
#Singleton
#Context
#Slf4j
#CompileStatic
class MasterScheduler{
#PostConstruct
void init(){
// Quartz Job initialization code written here. This works fine.
}
}
my build.gradle
dependencyManagement {
imports {
mavenBom 'io.micronaut:micronaut-bom:1.3.2'
}
}
dependencies {
annotationProcessor("io.micronaut:micronaut-inject-java:1.3.2")
annotationProcessor("io.micronaut:micronaut-inject-groovy:1.3.2")
implementation("io.micronaut:micronaut-inject:1.3.2")
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
compile group: 'org.quartz-scheduler', name: 'quartz', version: '2.3.0'
... other dependencies
}
Java version: 1.8
Note: I am using Micronaut scheduling capabilities, but I need distributed execution support & hence moving to Quartz ...
you need to define a JobFactory.
package io.micronaut.quartz;
import io.micronaut.context.BeanContext;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Singleton;
#Singleton
public class MicronautJobFactory implements JobFactory {
private final Logger log = LoggerFactory.getLogger(getClass());
private final BeanContext beanContext;
public MicronautJobFactory(BeanContext beanContext) {
this.beanContext = beanContext;
}
#Override
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
JobDetail jobDetail = bundle.getJobDetail();
Class<? extends Job> jobClass = jobDetail.getJobClass();
try {
if (log.isDebugEnabled()) {
log.debug(
"Producing instance of Job '" + jobDetail.getKey() +
"', class=" + jobClass.getName());
}
return beanContext.getBean(jobClass);
} catch (Exception e) {
SchedulerException se = new SchedulerException(
"Problem instantiating class '"
+ jobDetail.getJobClass().getName() + "'", e);
throw se;
}
}
}
afterward when you define your scheduler you can will then have to add your factory builder to the quartz scheduler when you define it. This replaces the default builder that comes with Quartz.
All your jobs going forward will have to be defined using #Prototype or #Singleton annotation. By Default Quartz uses newInstance() which circumvents object defined by Micronaut, but this will defer the object building to Micronaut when the job is declared.
scheduler.setJobFactory(jobFactory);

Injecting a different bean during local development with Quarkus

With Spring and Micronaut, there are very concise ways to inject a different bean depending on what environment/profile an application is running in. I'm trying to do the same with Quarkus.
I've read this post: https://quarkus.io/blog/quarkus-dependency-injection/. And the process is alluded to in this StackOverflow post: How can I override a CDI bean in Quarkus for testing?. That last post says, "create bean in test directory".
My problem is slightly different. I'd like to inject a bean when in "development". In production, I'd like the default bean injected. From the docs, I can't see a way to have the app make this distinction.
If I have a default class like this:
#DefaultBean
#ApplicationScoped
class ProdProvider : SomeProvider {}
And I want to override it like this:
#Alternative
#Priority(1)
class DevProvider : SomeProvider {}
How can I make this happen only in dev mode?
In one case, I have a credential provider class that sets up Google's PubSub emulator while in local development. In production, I use a class that implements the same interface, but a real credential provider. The particular case that led me to asking this question, though is a a class that implements one method:
#ApplicationScoped
class VaultLoginJwtProvider : LoginJwtProvider {
#ConfigProperty(name = "vault.tokenPath")
private val jwtPath: String? = null
companion object {
val logger: Logger = LoggerFactory.getLogger("VaultTokenProvider")
}
override fun getLoginJwt(): Optional<String> {
logger.info("Using Vault Login JWT")
return try {
Optional.of(String(Files.readAllBytes(Paths.get(jwtPath))).trim { it <= ' ' })
} catch (e: Exception) {
logger.error("Could not read vault token at $jwtPath")
logger.error(e.printStackTrace().toString())
Optional.empty()
}
}
}
That class is injected into another class via constructor injection:
#Singleton
class JwtServiceImpl(
#RestClient val vaultClient: VaultClient,
#Inject val loginJwtProvider: LoginJwtProvider
) {
private var serviceJwt: String? = null
companion object {
val logger: Logger = LoggerFactory.getLogger("JwtServiceImpl")
}
private fun getLoginToken(): String? {
val vaultLogin = VaultLogin(
role = "user-service",
jwt = loginJwtProvider.getLoginJwt().get()
)
val loginResponse = vaultClient.login(vaultLogin)
return loginResponse.auth.clientToken
}
}
I'd like to inject more of a "mock" class while in development that just returns a static string. I could use ProfileManager.getActiveProfile(), but that has me mixing development concerns into my logic. And I don't feel that that has any place in my compiled production code.
This is possible in Micronaut by using the annotation #Requires(env = ["dev", "test"]). I did briefly look at using #Produces but the Oracle EE docs seemed a little bit difficult for me to grasp. If that's the solution, I'll dig in.
In case anybody else comes across this, this is how to do it: https://quarkus.io/guides/cdi-reference#enabling-beans-for-quarkus-build-profile
For example:
import javax.enterprise.inject.Produces;
import com.oi1p.common.EmailSender;
import com.oi1p.common.ErrorEmailSender;
import com.oi1p.common.LogOnlyEmailSender;
import io.quarkus.arc.DefaultBean;
import io.quarkus.arc.profile.IfBuildProfile;
#ApplicationScoped
public class Producers {
#Produces
#IfBuildProfile("dev")
public EmailSender logOnlyEmailSender() {
return new LogOnlyEmailSender();
}
#Produces
#DefaultBean
public EmailSender errorEmailSender() {
// TODO: implement a real email sender. This one explodes when poked.
return new ErrorEmailSender();
}
}
My solution is to create the final bean on my own inside a #javax.ws.rs.ext.Provider. Not as elegant as Micronaut #Requires, but well, it works.
Note that instance of SomeProvider is not a "bean", you have to care for the lifecycle on your own (dependency injection, PostConstruct, no PreDestroy, ...).
org.acme.SomeProvider.java
package org.acme;
import javax.enterprise.context.ApplicationScoped;
public interface SomeProvider {
void providerMethod();
#ApplicationScoped
class ProdProviderRequirement {
void foo() {}
}
class ProdProvider implements SomeProvider {
private final ProdProviderRequirement prodProviderRequirement;
ProdProvider(final ProdProviderRequirement prodProviderRequirement) {
this.prodProviderRequirement = prodProviderRequirement;
}
#Override
public void providerMethod() {
prodProviderRequirement.foo();
}
}
class DevProvider implements SomeProvider {
#Override
public void providerMethod() {}
}
}
org.acme.SomeProviderFactory.java
package org.acme;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.ws.rs.ext.Provider;
import org.acme.SomeProvider.DevProvider;
import org.acme.SomeProvider.ProdProvider;
import org.acme.SomeProvider.ProdProviderRequirement;
#Provider
class SomeProviderFactory {
SomeProvider someProvider;
#Inject
SomeProviderFactory(final ProdProviderRequirement prodProviderRequirement) {
final var someCondition = true;
someProvider = someCondition ? new DevProvider() : new ProdProvider(prodProviderRequirement);
}
#Produces
#ApplicationScoped
SomeProvider someProvider() {
return someProvider;
}
}

i want to launch a class method periodically using spring

i have the following code.
#Configuration
#EnableAsync
#EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(10000);
executor.setThreadNamePrefix("Executor-");
executor.initialize();
return executor;
}
}
and if i want to run the recommend method after every certain interval of time. What can be the java spring bean configuration way to do that.?
public class UserBrandsRecommender {
public List<RecommendedItem> recommend(Long userId, int number) throws TasteException{
}
}
You should look into the #Scheduled annotation. For example:
#Scheduled(fixedDelay=5000)
public void doSomething() {
// something that should execute periodically
}
You'll probably need to create a new Spring bean with a method similar to above. The bean could have the UserBrandsRecommender injected into it. The new bean will need to implement some logic to pass proper values for the "userId" and "number" parameters to the "recommend" method.
More information here:
http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/htmlsingle/#scheduling-annotation-support

Resources