So, I have 2 schedulers set on different Spring profiles. When I run Spring scheduler everything works fine, but they want me to implement Quartz.
Here's a Job class:
#Profile("quartz")
#Component
public class SampleJob implements Job {
#Autowired
private GetDataServiceQuartz getDataServiceQuartz;
public SampleJob() {
}
public SampleJob(GetDataServiceQuartz getDataServiceQuartz) {
this.getDataServiceQuartz = getDataServiceQuartz;
}
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
this.getDataServiceQuartz.storeData();
}
}
An error being thrown:
org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.2.1.jar:na]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.2.1.jar:na]
Caused by: java.lang.NullPointerException: null
at com.example.blockchaininfo.services.Quartz.SampleJob.execute(SampleJob.java:27) ~[classes/:na]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.2.1.jar:na]
... 1 common frames omitted
A nullPointerException is being thrown on this particular line:
this.getDataServiceQuartz.storeData();
As soon as I've tried to print this.getDataServiceQuartz it prints null.
The class that does all the behind work:
#Slf4j
#Service
#Profile("quartz")
public class GetDataServiceQuartz{
constructorHere();
public void storeData(){
try {
String hashrateFromApi = this.getNetworkHashrateFromApi("http://public.turtlenode.io:11898/getinfo");
OffsetDateTime date = OffsetDateTime.now();
this.saveNetworkHashrateNewEntity(hashrateFromApi, date);
this.storePoolDataToDB(this.getPoolsListFromJson(), retrieveNetworkIdForPoolDefinition(date));
} catch (HttpServerErrorException e){
log.info("Network Server error e1: " + e);
} catch (ResourceAccessException e2){
log.info("Network resource access exception: " + e2);
} catch (IOException e3) {
log.info("" + e3);
} catch (InterruptedException e4){
log.info("" + e4);
}
}
...all other methods to acquire stuff.
And Quartz configuration.
#Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent){
this.startQuartzScheduling();
}
public void startQuartzScheduling () {
JobDetail job = JobBuilder.newJob(SampleJob.class)
.withIdentity("dummyJobName", "group1").build();
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("dummyTriggerName", "group1")
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(5).repeatForever())
.build();
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
} catch (SchedulerException e){
log.info("" + e);
}
}
What am I missing? How to properly inject a class that should have its methods scheduled?
I believe this is hapenning because quartz JobBuilder creates new instance of your SampleJob instead of using created one with autowired fields. And as it uses default constructor as the result you have nullpointer.
One option to fix this is to put your GetDataServiceQuartz to scheduler context.
Described here
So, to put your data you need to call:
scheduler.getContext().put("getDataServiceQuartz", getDataServiceQuartz);
And when executing your task:
SchedulerContext schedulerContext = jobExecutionContext.getScheduler().getContext();
schedulerContext.get("getDataServiceQuartz");
Other, in my opinion more convenient way, is to put it to JobDataMap which will be available from your SampleJob:
job.getJobDataMap().put("getDataServiceQuartz", getDataServiceQuartz);
When executing task:
context.getJobDetail().getJobDataMap().get("getDataServiceQuartz")
Full example can be found here.
Related
I am NOT able to stop an JMS consumer dynamically using a Spring Boot REST endpoint.
The number of consumers stays as is. No exceptions either.
IBM MQ Version: 9.2.0.5
pom.xml
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>mq-jms-spring-boot-starter</artifactId>
<version>2.0.8</version>
</dependency>
JmsConfig.java
#Configuration
#EnableJms
#Log4j2
public class JmsConfig {
#Bean
public MQQueueConnectionFactory mqQueueConnectionFactory() {
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setHostName("my-ibm-mq-host.com");
try {
mqQueueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
mqQueueConnectionFactory.setCCSID(1208);
mqQueueConnectionFactory.setChannel("my-channel");
mqQueueConnectionFactory.setPort(1234);
mqQueueConnectionFactory.setQueueManager("my-QM");
} catch (Exception e) {
log.error("Exception while creating JMS connecion...", e.getMessage());
}
return mqQueueConnectionFactory;
}
}
JmsListenerConfig.java
#Configuration
#Log4j2
public class JmsListenerConfig implements JmsListenerConfigurer {
#Autowired
private JmsConfig jmsConfig;
private Map<String, String> queueMap = new HashMap<>();
#Bean
public DefaultJmsListenerContainerFactory mqJmsListenerContainerFactory() throws JMSException {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(jmsConfig.mqQueueConnectionFactory());
factory.setDestinationResolver(new DynamicDestinationResolver());
factory.setSessionTransacted(true);
factory.setConcurrency("5");
return factory;
}
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
queueMap.put("my-queue-101", "101");
log.info("queueMap: " + queueMap);
queueMap.entrySet().forEach(e -> {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setDestination(e.getKey());
endpoint.setId(e.getValue());
try {
log.info("Reading message....");
endpoint.setMessageListener(message -> {
try {
log.info("Receieved ID: {} Destination {}", message.getJMSMessageID(), message.getJMSDestination());
} catch (JMSException ex) {
log.error("Exception while reading message - " + ex.getMessage());
}
});
registrar.setContainerFactory(mqJmsListenerContainerFactory());
} catch (JMSException ex) {
log.error("Exception while reading message - " + ex.getMessage());
}
registrar.registerEndpoint(endpoint);
});
}
}
JmsController.java
#RestController
#RequestMapping("/jms")
#Log4j2
public class JmsController {
#Autowired
ApplicationContext context;
#RequestMapping(value = "/stop", method = RequestMethod.GET)
public #ResponseBody
String haltJmsListener() {
JmsListenerEndpointRegistry listenerEndpointRegistry = context.getBean(JmsListenerEndpointRegistry.class);
Set<String> containerIds = listenerEndpointRegistry.getListenerContainerIds();
log.info("containerIds: " + containerIds);
//stops all consumers
listenerEndpointRegistry.stop(); //DOESN'T WORK :(
//stops a consumer by id, used when there are multiple consumers and want to stop them individually
//listenerEndpointRegistry.getListenerContainer("101").stop(); //DOESN'T WORK EITHER :(
return "Jms Listener stopped";
}
}
Here is the result that I noticed.
Initial # of consumers: 0 (as expected)
After server startup and queue connection, total # of consumers: 1 (as expected)
After hitting http://localhost:8080/jms/stop endpoint, total # of consumers: 1 (NOT as expected, should go back to 0)
Am I missing any configuration ?
You need to also call shutDown on the container; see my comment on this answer DefaultMessageListenerContainer's "isActive" vs "isRunning"
start()/stop() set/reset running; initialize()/shutDown() set/reset active. It depends on what your requirements are. stop() just stops the consumers from getting new messages, but the consumers still exist. shutDown() closes the consumers. Most people call stop + shutdown and then initialize + start to restart. But if you just want to stop consuming for a short time, stop/start is all you need.
You will need to iterate over the containers and cast them to call shutDown().
I am trying to handle the exception thrown from the Rabbit Listener and expose the error to the Rest endpoint.
Rabbit Listener
#Queue(ProductTopicConstants.GET_PRODUCT)
public ProductViewModel find(String id) {
try {
LOG.info(String.format("Listener --> Getting product for specified id = %s", id));
ProductSearchCriteria criteria = new ProductSearchCriteria();
criteria.setId(id);
Bson query = QueryBuilder.QueryBuilder(criteria, Bson.class).get(0);
ProductViewModel productViewModel = Single.fromPublisher(
repository.getCollection(ProductConstrants.PRODUCT_COLLECTION_NAME, Product.class)
.find(query)).map(successValue -> {
return new ProductViewModel(
successValue.getId().toString(),
successValue.getName(),
successValue.getDescription(),
successValue.getPrice()
);
}).blockingGet();
return productViewModel;
} catch (Exception ex) {
throw ex;
}
}
From the listener I have thrown an exception and it is handle in the RabbitListenerExceptionHandler as shown below
#Singleton
#Primary
#Replaces(DefaultRabbitListenerExceptionHandler.class)
public class RabbitListenerExceptionHandler implements io.micronaut.rabbitmq.exception.RabbitListenerExceptionHandler {
private static final Logger LOG = LoggerFactory.getLogger(RabbitListenerExceptionHandler.class);
#Override
public void handle(RabbitListenerException exception) {
if (LOG.isErrorEnabled()) {
Optional<RabbitConsumerState> messageState = exception.getMessageState();
if (messageState.isPresent()) {
//LOG.error("Error processing a message for RabbitMQ consumer [" + exception.getListener() + "]", exception);
throw new GlobalException();
} else {
LOG.error("RabbitMQ consumer [" + exception.getListener() + "] produced an error", exception);
}
}
}
}
Now I have a Global exception handler where it handles HTTP Request and Response.
public class GlobalException extends RuntimeException{
}
#Produces
#Singleton
#Requires(classes = {GlobalException.class, ExceptionHandler.class})
public class GlobalExceptionHandler implements ExceptionHandler<GlobalException, HttpResponse> {
#Override
public HttpResponse handle(HttpRequest request, GlobalException exception) {
return HttpResponse.ok(0);
}
}
When I have throw new GlobalException(); from RabbitListenerExceptionHandler the exception is not watched in the GlobalExceptionHandler
I get an exception as
fete.bird.common.extension.GlobalException: null
at fete.bird.common.extension.RabbitListenerExceptionHandler.handle(RabbitListenerExceptionHandler.java:25)
at fete.bird.common.extension.RabbitListenerExceptionHandler.handle(RabbitListenerExceptionHandler.java:14)
at io.micronaut.rabbitmq.intercept.RabbitMQConsumerAdvice.handleException(RabbitMQConsumerAdvice.java:343)
at io.micronaut.rabbitmq.intercept.RabbitMQConsumerAdvice.access$600(RabbitMQConsumerAdvice.java:67)
at io.micronaut.rabbitmq.intercept.RabbitMQConsumerAdvice$1.doHandleDelivery(RabbitMQConsumerAdvice.java:255)
at io.micronaut.rabbitmq.intercept.RabbitMQConsumerAdvice$1.handleDelivery(RabbitMQConsumerAdvice.java:284)
at com.rabbitmq.client.impl.ConsumerDispatcher$5.run(ConsumerDispatcher.java:149)
at com.rabbitmq.client.impl.ConsumerWorkService$WorkPoolRunnable.run(ConsumerWorkService.java:104)
at io.micronaut.scheduling.instrument.InvocationInstrumenterWrappedRunnable.run(InvocationInstrumenterWrappedRunnable.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
at java.base/java.lang.Thread.run(Thread.java:832)
08:41:35.266 [RxComputationThreadPool-3] ERROR i.m.h.s.netty.RoutingInBoundHandler - Unexpected error occurred: The source did not signal an event for 10000000000 nanoseconds and has been terminated.
java.util.concurrent.TimeoutException: The source did not signal an event for 10000000000 nanoseconds and has been terminated.
at io.reactivex.internal.operators.flowable.FlowableTimeoutTimed$TimeoutSubscriber.onTimeout(FlowableTimeoutTimed.java:139)
at io.reactivex.internal.operators.flowable.FlowableTimeoutTimed$TimeoutTask.run(FlowableTimeoutTimed.java:170)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
at java.base/java.lang.Thread.run(Thread.java:832)
Exception handlers used for HTTP have no relationship to the ones used for handling messaging exceptions and they are not designed to be called as a result of exceptions thrown from listeners or listener exception handlers.
I am writing an application in Springboot to fetch records from a database and then call an external rest api to update records into some other table. This code is completed and working as expected. As I need to improve performance as well. I am trying implement mulithreading while calling API, so that I can send multiple records at a time.
Structure :
Fetch records from a table and Store it in a list ---> Loop over list ---> multi threaded call to API
ProvRecordProcessing.java : This call will fetch records from database and create a list and call to ProvRecordService.java
ProvRecordService.java : This call will handle all API logic..
After some research, I tried to implement below to make it multithreaded :
Make ProvRecordService class to implement Runnable and override void run method
Instead of calling method, calling executorService.execute(new ProvRecordService(record));
ProvRecordProcessing.java :
I have removed other business logic from the code, only keep part where calling API method..
#Component
public class ProvRecordProcessing {
.....Code to fetch records from database....
List<UpdateProvider> provRecords = jdbcTemplate.query(sqlApiSelectQuery, new ProvRecordMapper());
//added for multithreading
ExecutorService executorService = Executors.newFixedThreadPool(2);
//looping over list records and calling API to process records
for(UpdateProvider record : provRecords) {
executorService.execute(new ProvRecordService(record));
}
executorService.shutdown();
}
}
ProvRecordService.java
Just to make it multithreaded, I have added few sections in the below code with comment : //added for multithreading
package com.emerald.paymentengineapi.service;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Service
public class ProvRecordService implements IFxiProviderService, Runnable {
#Autowired
RestSslException restSslTemplate;
#Autowired
DbConfig dbConfig;
#Autowired
UpdateProvider updateProvider; // added for multithreading
#Autowired
JdbcTemplate jdbcTemplate;
#Autowired
TokenService tokenService;
#Value("${SHIELD_API_URL}")
private String SHIELD_API_URL;
#Value("${token_expire_time}")
private String token_expire;
RestTemplate restTemplate;
DataSource dataSource;
UpdateProvider record; // added for multithreading
Logger logger = LoggerFactory.getLogger(ProvRecordService.class);
private static String FETCH_OPTIONS_SQL = "select OPTION_NAME, OPTION_VALUE from FSG.FSG_PRCB_PE_API_REQ_CONFIG";
public ProvRecordService(UpdateProvider record) { // added for multithreading
// TODO Auto-generated constructor stub
this.record = record;
}
#Override
public void run() { // added for multithreading
updateProvider(record);
}
#Scheduled(fixedRateString = "token_expire")
public ResponseEntity<String> runTokenScheduler() throws KeyManagementException, KeyStoreException, NoSuchAlgorithmException {
logger.info("Fetching Token..." + token_expire);
ResponseEntity<String> response = tokenService.getOauth2Token();
return response;
}
#Override
public ResponseEntity<String> updateProvider(UpdateProvider updateProviderRequest) {
dataSource = dbConfig.dataSource();
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
try {
restTemplate = restSslTemplate.restTemplate();
} catch (KeyManagementException | KeyStoreException | NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ResponseEntity<String> response = null;
try {
if (null == TokenService.TOKEN_VALUE.get(ConfigConstants.ACCESS_TOKEN))
runTokenScheduler();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
System.out.println("value :" + TokenService.TOKEN_VALUE.get(ConfigConstants.TOKEN_TYPE));
System.out.println("access_token :" + TokenService.TOKEN_VALUE.get(ConfigConstants.ACCESS_TOKEN));
headers.add(ConfigConstants.AUTHORIZATION, TokenService.TOKEN_VALUE.get(ConfigConstants.TOKEN_TYPE) + " "
+ TokenService.TOKEN_VALUE.get(ConfigConstants.ACCESS_TOKEN));
headers.add(ConfigConstants.CLIENT_CODE, ConfigConstants.CSP_PROVIDER_BATCH);
List<RequestOptions> customers = jdbcTemplate.query(FETCH_OPTIONS_SQL,new BeanPropertyRowMapper(RequestOptions.class));
updateProviderRequest.getXpfRequestData().setRequestOptions(customers);
HttpEntity<UpdateProvider> entity = new HttpEntity<UpdateProvider>(updateProviderRequest, headers);
response = restTemplate.exchange(SHIELD_API_URL, HttpMethod.PUT, entity, String.class);
if (response.getStatusCode() == HttpStatus.NO_CONTENT) {
logger.info(updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId());
logger.info(updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
updateStatusInDB(String.valueOf(response.getStatusCodeValue()), "NO_CONTENT",
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
logger.info("Provider has been updated successfully");
} else if (response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) {
updateStatusInDB(String.valueOf(response.getStatusCodeValue()), "INTERNAL_SERVER_ERROR",
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
logger.error("Internal Server error occures");
} else if (response.getStatusCode() == HttpStatus.NOT_FOUND) {
updateStatusInDB(String.valueOf(response.getStatusCodeValue()), "NOT_FOUND",
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
logger.error("Provider not found");
}
} catch (TokenServiceException ex) {
logger.error("Exception occures in calling Token API");
updateStatusInDB(ex.getMessage(), ex.getLocalizedMessage(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
//throw new RuntimeException("Exception occures in API " + ex);
} catch (HttpClientErrorException ex) {
logger.error("HttpClientErrorException occures in calling API");
updateStatusInDB(ex.getStatusText(), ex.getStatusText(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
//throw new HttpClientErrorException(ex.getStatusCode(), ex.getStatusText());
} catch (Exception ex) {
logger.error("Exception occures in calling API");
updateStatusInDB(ex.getMessage(), ex.getMessage(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getTaxId(),
updateProviderRequest.getXpfRequestData().getGroupRecord().getProviderData().getProviderId());
//throw new RuntimeException("Exception occures in API " + ex);
}
return response;
}
private int updateStatusInDB(String errorCode, String errorMessage, String taxId, String providerId) {
return jdbcTemplate.update(
"update FSG_WRK.FSG_PRCB_PE_API_REQUEST set ERRORCODE = ?, ERRORMESSAGE = ? where TAXID = ? and PROVIDERID= ?",
errorCode, errorMessage, taxId, providerId);
}
}
I debug this code , and it's going void run method and record is also getting populated , but after that, it's not going into the updateProvider method for processing and I am getting below error :
Exception in thread "pool-2-thread-1" java.lang.NullPointerException
at com.emerald.paymentengineapi.service.ProvRecordService.updateProvider(ProvRecordService.java:92)
at com.emerald.paymentengineapi.service.ProvRecordService.run(ProvRecordService.java:78)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Exception in thread "pool-2-thread-2" java.lang.NullPointerException
at com.emerald.paymentengineapi.service.ProvRecordService.updateProvider(ProvRecordService.java:92)
at com.emerald.paymentengineapi.service.ProvRecordService.run(ProvRecordService.java:78)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Exception in thread "pool-2-thread-3" java.lang.NullPointerException
at com.emerald.paymentengineapi.service.ProvRecordService.updateProvider(ProvRecordService.java:92)
at com.emerald.paymentengineapi.service.ProvRecordService.run(ProvRecordService.java:78)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Exception in thread "pool-2-thread-5" java.lang.NullPointerException
at com.emerald.paymentengineapi.service.ProvRecordService.updateProvider(ProvRecordService.java:92)
at com.emerald.paymentengineapi.service.ProvRecordService.run(ProvRecordService.java:78)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Update :
After more debugging, I got to know, the issue is occurring on the below line :
dataSource = dbConfig.dataSource();
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
I am trying to set dataSource here and this was working fine, when I haven't added code for multithreading. I am not able to get the reason. Please suggest.
The code is wrong here:
There is no point to create new ExecutorService per request execution.
a better approach would be creating it only once and keeping it as a data field of the ProvRecordProcessing component. Creating threads is expensive + in your approach, you don't know how many threads can be created simultaneously (what if this method is called by many users in parallel - if each creates thread pool you it can be really expensive).
In addition to the above if you use the thread pool executor you should ideally close it when the application shuts down, so don't forget to call close on predestroy or something.
Don't create a service with a new keyword, Spring won't be able to manage it and won't "process" any annotation of it (Autowired, Value, etc), so this code is wrong:
for(UpdateProvider record : provRecords) {
executorService.execute(new ProvRecordService(record));
}
Instead, Inject the service into ProvRecordProcessing Component as a singleton and call its method responsible for sending http request from runnable / callable. Here is a schematic example of what I mean:
#Component
class ProvRecordProcessing {
#Autowired
private ProvRecordService provRecordService;
....
for(UpdateProvider record : provRecords) {
executorService.execute(() -> provRecordService.updateHttpOrWhatever(record));
}
}
With this approach, ProvRecordService becomes a regular spring managed bean.
There are more advanced solutions for this, namely using #Async methods that can eliminate the need to "manually" maintain the thread pool. See This tutorial for example... Since you haven't shown those in a question, I assume it's beyond the scope of what you're asking so just keep in mind that it also exists. Of course, if you implement your code right it will do just fine.
My application is using spring boot with batch and testing it in aws lambda I want run the job in main method and NOT through scheduler. Is it possible to do that?
#SpringBootApplication
#EnableAutoConfiguration
#EnableJpaRepositories("com.myrepo.repository")
#ComponentScan("com.myrepo")
#EnableScheduling
public class Main {
private static final Logger LOG = LoggerFactory.getLogger(hMain.class);
#Autowired
JobLauncher launcher;
#Autowired
Job job;
public static void main(String[] args) {
try {
LOG.info("Start of application - debt card notofication JOB");
SpringApplication.run(BatchMain.class, args);
} catch (Exception e) {
LOG.error("Exception caught bathch Main, );
}
}
}
EDIT -- I wrote below code but it is not working inside aws lambda function
#Scheduled(cron = "0/1 * * * * *")
public void performBatchOpertaion() {
try {
LOG.info("Scheduling Job and Launcher {}, {}", job, launcher);
JobParameters params = new JobParametersBuilder()
.addString(Constants.MYBATCH, String.valueOf(System.currentTimeMillis()))
.toJobParameters();
launcher.run(job, params);
} catch (Exception e) {
LOG.error("Unable to schedules ", e.getCause());
}
}
public static void startApp() {
LOG.info("start batch job ");
SpringApplication.run(Main.class);
LOG.info("end batch job ");
}
here is my Request handler class which call statApp() of Main class
--------------------------------------------------------
public class MyHandler implements RequestHandler<Map<String, Object>, String> {
private static final Logger LOG = LoggerFactory.getLogger(MyHandler.class);
#Autowired
BatchMain main;
#Override
public String handleRequest(Map<String, Object> input, Context context) {
LOG.info("Inside the handler request");
BatchMain.startApp();
LOG.info("End of handler request");
return "End Of Application";
}
}
handleRequestmethod wait until the BatchMain to complete. You can use Thread join . Lambda execution engine suspend the main thread once the handle request return the result . In the next event it may complete the previous suspended tasks
I want to catch a hibernate database exception in my quartz job class, please find my quartz listener and quart job class, I'm trying to test my quartz scheduler with database failure scenario, while executing my quartz job I'm stopping mysql db to verify my quartz job class catching hibernate exception or not.
But the code below does not catch if any db exception occurs.
Can someone help what I'm doing wrong here:
MyListener.java
#WebListener
public class MyListener extends QuartzInitializerListener {
private final Logger logger = LoggerFactory.getLogger(MyListener.class);
private Scheduler scheduler;
#Override
public void contextInitialized(ServletContextEvent sce) {
super.contextInitialized(sce);
ServletContext ctx = sce.getServletContext();
StdSchedulerFactory factory = (StdSchedulerFactory) ctx.getAttribute(QUARTZ_FACTORY_KEY);
try {
scheduler = factory.getScheduler();
JobDetail job = newJob(TestQuartzJob.class)
.withIdentity("job1", "group1")
.build();
CronTrigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(cronSchedule("0/20 * * * * ?"))
.build();
scheduler.scheduleJob(job, trigger);
scheduler.start();
} catch (Exception e) {
ctx.log("There was an error scheduling the job.", e);
}
}
TestQuartzJob.java
public class TestQuartzJob implements Job throws JobExecutionException{
Logger logger = Logger.getLogger(TestQuartzJob.class);
Session mysession = HibernateUtilities.getJobsSessionFactory().openSession();
Transaction transaction = mysession.beginTransaction();
#Override
public void execute(JobExecutionContext jec) {
try {
Student getstudName = (Student) mysession.
createCriteria(Student).add(eq(“student_id", “stud1234")).uniqueResult();
String name = getstudName.getName();
} catch (Exception e) {
logger.info("--- Error in job!");
JobExecutionException e2 = new JobExecutionException(e);
// this job will refire immediately
e2.refireImmediately();
throw e2;
}
}
}