Rest client closed - quarkus

I am using Quarkus 2.10.2 and reactive rest clients. One of the rest clients (pas2clabService) is created using the RestClientBuilder and closed after the request in the IntegrationController has finished.
#RequestScoped
public class IntegrationController {
PAS2CLabService pas2clabService;
#PostConstruct
void postConstruct() {
this.pas2clabService = RestClientBuilder.newBuilder().baseUri(URI.create(integration.getUrl()))
.build(PAS2CLabService.class);
}
#PreDestroy
void preDestroy() {
this.pas2clabService.close();
}
public Uni<Optional<OverviewDocument>> getPAS2CLabOverview(String labCode) {
return pas2clabService.getWorklist()
.onItem().transform(response -> translatePAS2CLabWorklistResponse(response, labCode))
.onFailure().recoverWithItem(this::translatePAS2CLabWorklistResponse);
}
}
#RegisterRestClient(configKey = Integrations.PAS2CLAB_API)
#RegisterClientHeaders(PAS2CLabHeaderFactory.class)
#RegisterProvider(PASRestClientExceptionMapper.class)
public interface PAS2CLabService extends AutoCloseable {
#GET
#Path("/v1/worklist")
#Produces("application/json")
Uni<PAS2CLabWorklistResponse> getWorklist();
}
This approach has worked greatly with Quarkus 2.9.2 and older version, but since 2.10.2 the rest client is already closed when I invoke it via getPAS2CLabOverview. I can work around the problem by using #Singleton or #ApplicationScoped in the IntegrationController as this postpones the #PreDestroy event. I would however like to understand why I cannot use the #RequestScoped anymore and #PreDestroy is triggered before the Uni received an item.

Related

What is the difference between TransactionManager.begin() and QuarkusTransaction.begin()?

Quarkus 2.8.0.Final introduced QuarkusTransaction. What is the difference between
#ApplicationScoped
public class MyClass {
#Inject
TransactionManager tm;
public void doSomething() throws Exception {
tm.begin();
// ...
tm.commit();
}
}
and
#ApplicationScoped
public class MyClass {
public void doSomething() {
QuarkusTransaction.begin();
// ...
QuarkusTransaction.commit();
}
}
?
I am using the TransactionManager in a lot of my tests, and when I replaced it with QuarkusTransaction, I am getting different error messages when something fails:
When using the TransactionManager, I am getting
javax.transaction.NotSupportedException: BaseTransaction.checkTransactionState - ARJUNA016051: thread is already associated with a transaction!
When using QuarkusTransaction, I am getting
javax.enterprise.context.ContextNotActiveException
The Quarkus documentation does not really explain why QuarkusTransaction was introcuded 🤔
QuarkusTransaction was introduced with this Pull Request and the idea is to provide an easier to use Transactions API.
As can be seen in this test, it's meant to be used when a request scope is active

Spring Boot with javax Event

I try to get spring boot working with cdi events.
I have the following class which fires the event.
#Component
#UIScope
public class Login extends LoginOverlay
{
#Autowired
private UserInfo userInfo;
#Inject
private Event<UpdateCWViewEvent> cwevent;
#PostConstruct
public void init()
{
addLoginListener(new ComponentEventListener<LoginEvent>()
{
#Override
public void onComponentEvent(LoginEvent event)
{
userInfo.login(event.getUsername(), event.getPassword());
if (userInfo.isLoggedIn())
{
setButtonLabel();
close();
cwevent.fire(new UpdateCWViewEvent());
}
}
});
}
}
And in another class the following method
public void update(#Observes(notifyObserver=Reception.IF_EXISTS) UpdateCWViewEvent event)
{
//do something
}
Now I have the following problem. I need an Implementation of javax.enterprise.event.Event. I tried to take weld and use the standard Eventimpl. Now, I tried to configure a Spring Configuration class to tell my application, there is an implementation of my event.
#Configuration
public class Config
{
#Bean
public Event<UpdateCWViewEvent> cwEvent()
{
//return EventImpl.of(injectionPoint, beanManagerImpl);
}
}
I dont know what to do with the injectionPoint and beanManagerImpl. Does anybody of you had the same problem and solved it? Or does anybody have an alternative to fire easy cdi events in a spring boot application?
Thank you very much and stay healthy!

Spring Cloud Stream - First Kafka messages get error "Dispatcher has no subscribers"

My app successfully sends Kafka messages, but only after Kafka is initialized. Before that i get the error "Dispatcher has no subscribers". How do i wait for subscribers to finish being registered for channels?
Here's a trace of the order of events (timing in second.ms):
17.165 SenderClass created
17.816 initialization class, #PostConstruct starts PollingTask
24.781 PollingTask sends first Kafka message
24.816 First error: "Dispatcher has no subscribers"
25.778 Registering MessageChannel my-channel
still seeing Dispatcher errors
27.067 Channel my-channel' has 1 subscriber
No more errors after this, messages send fine
i'm not sure how to approach this. Wild guesses have included:
Place sending code in #PostConstruct
Add #AutoConfigureBefore(BindingServiceConfiguration.class) to Sender
Add #AutoConfigureAfter(BindingServiceConfiguration.class) to SenderClass
Add #AutoConfigureBefore(BindingServiceConfiguration.class) to Main
Place #DependsOn({"EnableBindingClass"}) on Task
Place #DependsOn({"ApplicationLifeCycle"}) on SchedulerClass, where ApplicationLifeCycle is a class that does nothing but
implements SmartLifecycle with getPhase returning MAX_INT
Making sure ComponentScan is on for whole package (a suggestion from other SO threads)
Various combinations of the above
Created a new app, made it as simple as i could:
public interface Source {
#Output(channelName)
MessageChannel outboundChannel();
}
#EnableBinding(Source.class)
#Component
public class Sender {
#Autowired
private Source source;
public boolean send(SomeObject object) {
return source.outboundChannel().send(MessageBuilder.withPayload(object).build());
}
#Service
public class Scheduler {
#Autowired
Sender sender;
#Autowired
ThreadPoolTaskScheduler taskScheduler;
#PostConstruct
public void initialize() {
taskScheduler.schedule(new PollingTask(), nextTime);
}
private class PollingTask implements Runnable {
#Override
public void run() {
List<SomeObject> objects = getDummyData();
for(SomeObject object : objects)
{
sender.send(interval);
}
Instant nextTime = Instant.now().plusMillis(1_000L);
try {
taskScheduler.schedule(new PollingTask(), nextTime);
} catch (Exception e) {
logger.error(e);
}
}
}
Edit to add Solution
It works now! In my scheduler that starts the things that send the messages i switched from starting things in #PostConstruct to SmartLifecycle::start().
#Service
public class Scheduler implements SmartLifecycle {
#Autowired
Sender sender;
#Autowired
ThreadPoolTaskScheduler taskScheduler;
#Override
public void start() {
taskScheduler.schedule(new PollingTask(), nextTime);
}
private class PollingTask implements Runnable {
#Override
public void run() {
List<SomeObject> objects = getDummyData();
for(SomeObject object : objects)
{
sender.send(interval);
}
Instant nextTime = Instant.now().plusMillis(1_000L);
try {
taskScheduler.schedule(new PollingTask(), nextTime);
} catch (Exception e) {
logger.error(e);
}
}
}
#PostConstruct is too early to send messages; the context is still being built.. Implememt SmartLifecycle, put the bean in a high phase (Integer.MAX_VALUE) and do the sends in start().
Or do the sends in an ApplicationRunner.
I faced a similar problem in Webflux + Spring Cloud Stream functional style. Spring Cloud Function in 2022 is the preferred way. ​
My hypothesis after a lot of debugging was that beans were not created in right order. The bean was probably not registered in spring-cloud-stream's dispatchers before kafka message processing started. similar to what #gary mentioned.
So I added #Order(1) before my consumer beans. Hoping that this bean would be created before it is dispatcher-registrations starts.
​#Bean
​#Order(1)
​public Function<Flux<Message<Pojo>>, Mono<Void>> pojoConsumer() {
This seems to fix my issue for now.

How to have per-thread but reusable objects (PubNub) in a Spring app?

I'm connecting to PubNub in a Spring Boot application. From the documentation, it's ok to re-use PubNub objects but it's better to have one per thread. What's the appropriate method to store and retrieve one object per thread in Spring Boot?
This is how you'd store and retrieve an object per thread in Spring using ThreadLocal, this example is based on Spring's own ThreadLocalSecurityContextHolderStrategy which is used to store SecurityContext per thread.
Also, take a look at InheritableThreadLocal especially if your code spins up new thread, e.g. Spring's #Async annotation, it has mechanisms to propagate existing or create new thread local values when creating child threads.
import org.springframework.util.Assert;
final class ThreadLocalPubNubHolder {
private static final ThreadLocal<PubNub> contextHolder = new ThreadLocal<PubNub>();
public void clearContext() {
contextHolder.remove();
}
public PubNub getContext() {
PubNub ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
public void setContext(PubNub context) {
Assert.notNull(context, "Only non-null PubNub instances are permitted");
contextHolder.set(context);
}
public PubNub createEmptyContext() {
// TODO - insert code for creating a new PubNub object here
return new PubNubImpl();
}
}
You can use Java ThreadLocal support as mentioned above by #SergeyB. Another way to do it is to use Thread Scope for your beans:
#Configuration
public class AppConfig {
//Register thread scope for your application
#Bean
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope("thread", new SimpleThreadScope());
}
}
Then you can create a bean with a thread scope (proxy mode will be explained below):
#Scope(value = "thread", proxyMode = ScopedProxyMode.TARGET_CLASS)
#Component
public class PubSubContext {
private PubSub pubSub;
public PubSub getPubSub() {
return pubSub;
}
public void setPubSub(PubSub pubSub) {
this.pubSub = pubSub;
}
#PostConstruct
private void init() {
// TODO: your code for initializing PubSub object
log.info("RequiredMessageHeaders started in thread " + Thread.currentThread().getId());
}
#PreDestroy
private void destroy() {
// TODO: your code for cleaning resources if needed
log.info("RequiredMessageHeaders destroyed in thread " + Thread.currentThread().getId());
}
}
The last step is to inject PubSubContext where you need it:
#Controller
public class YourController {
// Spring will inject here different objects specific for each thread.
// Note that because we marked PubSubContext with proxyMode = ScopedProxyMode.TARGET_CLASS we do not need to use applicationContext.get(PubSubContext.class) to obtain a new bean for each thread - it will be handled by Spring automatically.
#Autowired
private PubSubContext pubSubContext;
#GetMapping
public String yourMethod(){
...
PubSub pubSub = pubSubContext.getPubSub();
...
}
}
With this approach, you could go even further and mark your PubSubContext as #Lazy, so it won't be created until it's requested inside yourMethod :
#Controller
public class YourController {
#Lazy
#Autowired
private PubSubContext pubSubContext;
...
}
As you see PubSubContext does basically what ThreadLocal does but leveraged by Spring capabilities.
Hope it helps!
First of all,
As it is safe to use single PubNub object in multiple threads,
You need multiple PubNub objects ONLY if you need performance increase
If that is your case - my suggestion will be to organize pool of PubNub objects (the use case is quite close to DB connection use case).

Problems injecting a BayeuxService into another class with annotations

I have a web app that is using Bayeux to handle Comet connections. I initialize a BayeuxServer and tie it into Spring annotations and it all works fine, listening on selected channels and responding.
I have a Jersey annotated class and an annotated Bayeux service as shown below. The idea is I wanted to be able to control resources via Rest from an individual web app, and then right after the resource is changed, do a server push via Comet to all other applicable clients to tell them to update their information.
Here is the problem: A Bayeux Service is created when the webapp is deployed, setting up proper channels to listen on and monitoring clients. There should only be one instance of this. When Jersey attempts to use the Bayeux service it creates a whole new service, when it should be using the original one. This new service doesn't have the BayeuxServer properly injected so I can't access client information through it.
It makes since that this should be doable, but I don't seem to understand how to inject these things properly via annotations. Can anyone point me in the right direction?
Jersey Annotated Class:
#Path("JsonTest")
public class JsonTest {
#Context
Request request;
#Context
UriInfo uriInfo;
#Context
ResourceContext resourceContext;
protected final Logger log = Logger.getLogger(getClass());
public JsonTest() {
}
#DELETE
#Path("{id}")
public void deleteJson(#PathParam("id") String id) {
JsonTestDao.instance.getModel().remove(id);
log.info("Deleted Json..." + id);
log.info("New json: " + JsonTestDao.instance.getModel().toString());
JsonTestService jsonTestService = resourceContext.getResource(JsonTestService.class);
jsonTestService.sendUpdate();
}
}
BayeuxService:
#Named
// Singleton here didn't seem to make a difference
#Service
public class JsonTestService {
protected final Logger log = Logger.getLogger(getClass());
#Inject
private BayeuxServer bayeux;
#Session
private ServerSession serverSession;
#PostConstruct
public void init() {
log.info("Initializing JsonTest Bayeux HelloService...");
log.info("Current sessions are: " + bayeux.getSessions().toString());
}
#Listener("/cometd/JsonTest")
public void jsonTestHandler(ServerSession remote, ServerMessage.Mutable message) {
}
public void sendUpdate() {
//bayeux.newMessage(); // Need a method that the Jersey class can call to notify changes
log.info("Bayeux server should be sending an update now...");
}
#PreDestroy
public void destroy() {
log.info("Destroying JsonTest Bayeux HelloService...");
}
}
See Jersey and spring integration - bean Injections are null at runtime.
Another question I asked. Both of these stem from the same problem involving properly setting the Jersey dependency and integrating it with spring.

Resources