Spring framework and Quartz scheduler - java

I have a Spring MVC web application and I want to make use of Quartz scheduler. After reading the docs on Quartz and also on how it integrates with Spring, I am left wondering.
Will Quartz handler run as a separate process independent of tomcat or is it just another maven dependency that I will add be able to schedule within my controllers?.
This is the tutorial that I am reading from https://dzone.com/articles/integrating-quartz-withspring

Quartz is just another maven dependency which starts a daemon thread in the background and keeps looking into QRTZ_CRON_TRIGGERS every defined interval, which stores when the job run last time, and when it is going to run next time. You can get more detailed diagram at http://www.javarticles.com/wp-content/uploads/2016/03/QuartzSchedulerModel.png which will help you understand, how it works internally.

I did it in spring REST service:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
#SpringBootApplication
#EnableScheduling
#ComponentScan(basePackages = "com.some")
public class ApiApplication {
public static void main(final String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
..............
#Component
public class ScheduledTasks {
#Scheduled(cron = "0 1 0 * * *")
public void expiredPromotionsTask() {
log.debug("expiredPromotionsTask begin");
try {
your code here..
log.debug("expiredPromotionsTask end");
} catch (final Exception e) {
log.error(e, "expiredPromotionsTask failed");
}
}

Related

Intelij IDEA 2022.1 Cannot debug Runtime.getRuntime().addShutdownHook();

I've tried to debug Runtime.getRuntime().addShutdownHook(unfortunateHook); in sample tapestry application created with Maven quickstart, but it seems that it doesn't work - Debugger doesn't stop at trap set to line set at LOGGER.debug("In the main hook!!!");
Example code:
package org.example.spring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
#SpringBootApplication
public class App extends SpringBootServletInitializer {
private static final Logger LOGGER= LoggerFactory.getLogger(App.class);
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(AppConfiguration.class);
}
public static void main(String[] args) throws Exception {
Thread unfortunateHook = new Thread(() -> {
LOGGER.debug("In the main hook!!!");
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
});
Runtime.getRuntime().addShutdownHook(unfortunateHook);
LOGGER.debug("Simple log statement with inputs {}, {} and {}", 1,2,3);
SpringApplication application = new SpringApplication(App.class);
SpringApplication.run(App.class, args);
}
}
Is it possible to debug code inside shutdown hook thread? I'm using InteliJ Idea 2022.01. Killing application with standard Intelij Idea way (red square).
Ok, after some more digging I have found this: https://youtrack.jetbrains.com/issue/IDEA-170313/Breakpoints-not-working-after-stop-signal and many more simmilar threads.
In short, Idea is disconnecting debugger in a moment that you click red square and sends signal to app to kill itself. Because of these, traps are not working any more.
Workaround to this is to send kill signal from external source. In Linux it's pretty easy but in windows (which is I'm using) you should:
Put a break point any place in your code that will be executed
When code stops at your trap, you go to "evaulate expression" (alt+f8 or from degug acctions) and write System.exit(0)
You breakpoint may need to be set up as a thread one - to do that, click right button on it and switch to "Thread"
Then breakpoint should work.

How to start multiple message consumers in Quarkus?

I'm trying to migrate from Vert.x to Quarkus and in Vert.x when I write message consumers like Kafka/AMQP etc. I have to scale the number of verticals to maximize performance across multiple cores i.e. Vertical Scaling - is this possible in Quarkus? I see a similar question here but it wasn't answered.
For example, with Kafka I might create a consumer inside a vertical and then scale that vertical say 10 times (that is specify the number of instances in the deployment to be 10) after doing performance testing to determine that's the optimal number. My understanding is that by default, 1 vertical = 1 event loop and does not scale across multiple cores.
I know that it's possible to use Vert.x verticals in Quarkus but is there another way to scale things like the number of Kafka consumers across multiple core?
I see that this type of scalability is configurable for things like Quarkus HTTP but I can't find anything about message consumers.
Here's the Vert.x Verticle approach that overall I'm very happy with, but I wish there were better documentation on how to do this.
UPDATE - Field injection doesn't work with this example but constructor injection does work.
Lets say I want to inject this
#ApplicationScoped
public class CoffeeRepositoryService {
public CoffeeRepositoryService() {
System.out.println("Injection succeeded!");
}
}
Here's my Verticle
package org.acme;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.vertx.core.AbstractVerticle;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.mutiny.core.eventbus.EventBus;
import io.vertx.mutiny.rabbitmq.RabbitMQClient;
import io.vertx.mutiny.rabbitmq.RabbitMQConsumer;
import io.vertx.rabbitmq.QueueOptions;
import io.vertx.rabbitmq.RabbitMQOptions;
public class RQVerticle extends AbstractVerticle {
private final Logger LOGGER = LoggerFactory.getLogger(org.acme.RQVerticle.class);
//This doesn't work - returns null
#Inject
CoffeeRepositoryService coffeeRepositoryService;
RQVerticle() {} // dummy constructor needed
#Inject // constructor injection - this does work
RQVerticle(CoffeeRepositoryService coffeeRepositoryService) {
//Here coffeeRepositoryService is injected properly
}
#Override
public Uni<Void> asyncStart() {
LOGGER.info(
"Creating RabbitMQ Connection after Quarkus successful initialization");
RabbitMQOptions config = new RabbitMQOptions();
config.setUri("amqp://localhost:5672");
RabbitMQClient client = RabbitMQClient.create(vertx, config);
Uni<Void> clientResp = client.start();
clientResp.subscribe()
.with(asyncResult -> {
LOGGER.info("RabbitMQ successfully connected!");
});
return clientResp;
}
}
Main Class - injection doesn't work like this
package org.acme;
import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;
import io.vertx.core.DeploymentOptions;
import io.vertx.mutiny.core.Vertx;
#QuarkusMain
public class Main {
public static void main(String... args) {
Quarkus.run(MyApp.class, args);
}
public static class MyApp implements QuarkusApplication {
#Override
public int run(String... args) throws Exception {
var vertx = Vertx.vertx();
System.out.println("Deployment Starting");
DeploymentOptions options = new DeploymentOptions()
.setInstances(2);
vertx.deployVerticleAndAwait(RQVerticle::new, options);
System.out.println("Deployment completed");
Quarkus.waitForExit();
return 0;
}
}
}
Main Class with working injection but cannot deploy more than one instance
package org.acme;
import io.quarkus.runtime.StartupEvent;
import io.vertx.mutiny.core.Vertx;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import org.jboss.logging.Logger;
#ApplicationScoped
public class MainVerticles {
private static final Logger LOGGER = Logger.getLogger(MainVerticles.class);
public void init(#Observes StartupEvent e, Vertx vertx, RQVerticle verticle) {
public void init(#Observes StartupEvent e, Vertx vertx, RQVerticle verticle) {
DeploymentOptions options = new DeploymentOptions()
.setInstances(2);
vertx.deployVerticle(verticle,options).await().indefinitely();
}
}
Std Out - first main class looks good
2021-09-15 15:48:12,052 INFO [org.acm.RQVerticle] (vert.x-eventloop-thread-2) Creating RabbitMQ Connection after Quarkus successful initialization
2021-09-15 15:48:12,053 INFO [org.acm.RQVerticle] (vert.x-eventloop-thread-3) Creating RabbitMQ Connection after Quarkus successful initialization
Std Out - second main class
2021-09-22 15:48:11,986 ERROR [io.qua.run.Application] (Quarkus Main
Thread) Failed to start application (with profile dev):
java.lang.IllegalArgumentException: Can't specify > 1 instances for
already created verticle

Spring Boot console application causes JUnit test case to wait

I have Spring Boot console application which accepts user input. In the same application, there are service to perform some logic.
On running the service's JUnit test case directly, I am observing a behavior where the console application stays in the state of accepting user input, and thus not entering the JUnit tests.
Usually there is no issue becasue for a normal Spring Boot application, there is nothing to run on start up. But for this console application setup, it awaits user input.
May I know if there is any way that can make the test cases run while not triggering the console application? Thank you very much.
Observation: The console application is triggered on JUnit test case run, test case is not entered
Main application
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ToyRobotApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ToyRobotApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// Instruct user to perform input
System.out.println("Welcome to toy robot application!");
System.out.println("Below are the possible operations:");
System.out.println("PLACE <x-coordinate> <y-coordinate> <facing>");
System.out.println("MOVE");
System.out.println("LEFT");
System.out.println("RIGHT");
System.out.println("REPORT");
while (true) {
System.out.println("Please enter your command:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String usrInput = br.readLine();
//...
}
}
}
Test case
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.somecompany.model.Facing;
import com.somecompany.model.Location;
import com.somecompany.model.Robot;
import com.somecompany.service.ToyRobotService;
#SpringBootTest
public class ToyRobotReportTest {
#Autowired
private Robot robot;
#Autowired
private ToyRobotService toyRobotService;
#Test
public void shouldBeAbleToReportLocation() {
Location location = new Location();
location.setXCor("1");
location.setYCor("2");
location.setFacing(Facing.NORTH);
robot.setLocation(location);
// Actual result
String result = toyRobotService.report();
// Assertion
assertEquals("1,2,NORTH", result);
}
}
Note: Other sources omitted
I got the setup working after some research. To get this to work, the JUnit test class should not be annotated with the usual #SpringBootTest annotation.
When the class is annotated with #SpringBootTest, it will start up the application's context (i.e. start the application). In most usual test scenarios, this should be fine if there is no waiting of user input.
In our case, we want the Spring Boot annotations (e.g. #Autowired) to be recognized, but do not want the application itself to be started up.
We can use the following in JUnit:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = ToyRobotApplication.class, initializers = ConfigDataApplicationContextInitializer.class)
public class ToyRobotPlaceTest {
...
}
This will regonize spring annotations, but will not start the application context.

Modify #JMSListener destination on-the-fly on Spring Boot

I have developed a #JMSListener that gets the destination from Java properties and works just fine.
But now I would need to be able to change the "destination" of the queue on runtime without having to reset the whole application, and even if I modify the Properties on runtime, the queue "destination" does not change.
Here is how We are implementing the #JMSListener:
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
#Component("b2b.CCRReceiver")
#Slf4j
public class CCRReceiver {
//SOME_VARIABLES
#Transactional
#JmsListener(destination = "${tibco.configuration.queues.upsert}", containerFactory = "jmsFactory", concurrency = "${jms.concurrency}")
public void receiveMessage(Message message) {
//DO_SOME_STUFF
}
}
As you can see, I get the destination from a Value Expression the first time and it works fine, but then I don't know how to access the JMSListener and change it's destination.
Can this be done? Is there any way to change the destination?
Or I will have to implement this JMS Listener in an other way that allows me to do this?
This should work:
Give the listener an id property
Auto wire the JmsListenerEndpointRegistry (or otherwise get a reference to it)
registry.getListenerContainer("myListener").stop();
registry.getListenerContainer("myListener").shutdown();
((AbstractMessageListenerContainer) registry.getListenerContainer("myListener"))
.setDestinationName("newOne")
registry.getListenerContainer("myListener").initialize();
registry.getListenerContainer("myListener").start();
I solve this problem work with a component Listener Thread. Using TaskExecutor and ApplicationContext to manage. You can create at runtime. I'm still working on it. I'll try Gary Russell's suggestion too.
Sorry about english. Feel free to correct.
applicationContext.getBean(ExampleListenerJMS.class);
...
taskExecutor.execute(exampleListenerJMS);
The class listener "implements Runnable, MessageListener" with a implementation getting custom connection managers (activemq servers different).
#Component
#Scope("application")
public class ExampleListenerJMS implements Runnable, MessageListener {
private EspecificManagerJMS jms = new EspecificManagerJMS();
#Override
public void run() {
customAndChekingActions();
}
protected void customAndChekingActions() {
...
try {
Destination destination = jms.getSession().createQueue(queue);
MessageConsumer consumer = jms.getSession().createConsumer(destination);
consumer.setMessageListener(this);
...
} catch (JMSException e) {
e.printStackTrace();
...
}
}
#Override
public void onMessage(Message message) {
...
}
I hope it helped you

Shutdown hooks in Spring

I am trying to add a shutdown hook via Runtime.getRuntime().addShutdownHook() in a Spring Gradle application.
I tried adding an anonymous thread subclass to the Application.java class from this exact tutorial So that it looks like this:
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
Thread thread = new Thread() {
public void run() {
System.out.println("Shutdown Thread!");
}
};
Runtime.getRuntime().addShutdownHook(thread);
SpringApplication.run(Application.class, args);
}
}
However, I am not seeing the desired behavior: printing "Shutdown Thread!" on exiting the application. What is going wrong here?
edit: I have been shutting it down using control + c on the terminal where it is running. I am running it on the OSX terminal

Categories

Resources