I'm working on a task in which I need to save job status in database but when I tried to do this it gives me java.lang.nullpointerexception. I think it happen because whenever I tried to select/save/update any record from the database only at that time it gives me error similar to this.
Here is my code
public class PostFBJob implements Job {
private SchedulerService schedulerService;
private SubCampaignService subCampaignService;
#SuppressWarnings("unchecked")
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDetail jobDetail = context.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
schedulerService = (SchedulerService) jobDataMap.get("schedulerService");
SubCampaign subCampaign = (SubCampaign) jobDataMap.get("subCampaign");
if (prStreamItem.getName().equalsIgnoreCase("Facebook") && StringUtils.isNotBlank(branch.getFbAccessToken())) {
FacebookService facebookService = FacebookService.getSingleton();
try {
subCampaign.setStatus("Completed");
subCampaign.setMessage("Completed");
subCampaignService.updateSubCampaign(subCampaign);
} catch (Exception e) {
log.error("", e);
}
}
}
}
Exception
java.lang.NullPointerException
at com.ace.Job.SubCampaignJob.execute(SubCampaignJob.java:147)
at org.quartz.core.JobRunShell.run(JobRunShell.java:213)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:557)
Error: java.lang.NullPointerException
Please help me to solve this issue. I'm new to Spring and Quartz.
Thanks in advance
This exception due to null object of subCampaignService.
Injection work only those objects which are managed by spring.
To scan your all packages where you want to use Service.
Annotation based configuration
#ComponentScan({"com.ace"})
XML based configuration
<context:component-scan base-package="com.ace"/>
And also check null before update like below:
try {
subCampaign.setStatus("Completed");
subCampaign.setMessage("Completed");
if(subCampaign != null && subCampaignService != null) //Check is not null to subCampaign before update
subCampaignService.updateSubCampaign(subCampaign);
} catch (Exception e) {
log.error("", e);
}
I think the answer you are looking for is here
Please check below link
Correct way to persist Quartz triggers in database
And you can also check this link
Using Hibernate session with quartz
Related
I have a quarkus project connecting to monogdb using reactive panache.
I would like my method to be wrapped in a transaction and my current code looks roughly as follows:
#Traced
#ApplicationScoped
#Startup
public class MyReceiver implements com.google.cloud.pubsub.v1.MessageRecevier {
#Override
#ActivateRequestContext
public void receiveMessage(PubsubMessage pubsubMessage, AckReplyConsumer ackReplyConsumer) {
try {
final String messageStr = pubsubMessage.getData().toStringUtf8();
final MyMessage messageContent = objectMapper.readValue(messageStr, getTypeReference());
handleMessage(messageContent).await().indefinitely();
ackReplyConsumer.ack();
} catch (Throwable ex) {
log.warn("{} Message ID: [{}] on [{}] ", ex.getMessage(), pubsubMessage.getMessageId(), subscriptionName);
ackReplyConsumer.nack();
}
}
public TypeReference<MyMessage> getTypeReference() {
return new TypeReference<>(){};
}
#ReactiveTransactional
public Uni<Void> handleMessage(MyMessage message) {
// code here is never reached
}
}
When i try to test my code however and get a message,
I am getting this error: java.lang.NullPointerException: Cannot invoke "org.hibernate.reactive.mutiny.Mutiny$Session.withTransaction(java.util.function.Function)" because "session" is null
And it happens when the code tries to go into handleMessage, so when the aspect for #ReactiveTransactional is being triggered
What can I look out for that is causing this cause I can't find anything that can be the source of the issue.
It seems at the moment, panache does not support transactions in mongodb which was the source of this issue.
So I would like to wrap a PessimisticLockingFailureException that gets thrown in a jpa repo when trying to get a lock for an entity that is already locked. And handle the wrapped exception in my exception handlers.
But it seems that when spring tries to end the transaction the connection is already closed and spring throws a new exception that overwrites the exception I would like to see.
In the logs I get "Application exception overridden by rollback exception" and it is this I would like to avoid. (Cause of rollback ex is that "Connection is closed")
Is there a solution to this? Or am I doing something wrong?
(Here's some pseudo code of what I'm doing)
String restControllerMethod(String args) {
try {
return service.serviceMethod(args);
} catch (Exception e1) {
throw e1; // org.springframework.orm.jpa.JpaSystemException caused by org.hibernate.TransactionException caused by java.sql.SQLException
}
}
#Transactional
String serviceMethod(String args) {
Entity entity;
try {
entity = repo.repoFindMethod(args);
} catch (Exception e2) {
throw new WrappingException(e2); // org.springframework.dao.PessimisticLockingFailureException caused by org.hibernate.PessimisticLockException
}
// do some processing with entity
return result;
}
#Lock(LockModeType.PESSIMISTIC_READ)
String repoFindMethod(String args);
I'm using spring-boot-starter-parent 2.3.2.RELEASE with spring-boot-starter-web spring-boot-starter-data-jpa and an emmbedded h2 db
Fixed this by adding a com.zaxxer.hikari.SQLExceptionOverride implementation and pointing the
spring.datasource.hikari.exception-override-class-name to it.
This causes hikari to not close the connection when the db throws an exception with the specified error code.
I've also added #QueryHints({#QueryHint(name = "javax.persistence.lock.timeout", value = "0")}) to the locking query since default lock wait times can be vendor specific
The issue with this solution is that it is vendor specific (both for h2 and hikari). And not all vendors support a custom timeout for obtaining locks (h2 for example does not support this but it matters less since it's timeout is very short anyway)
Example of my solution (for h2):
spring.datasource.hikari.exception-override-class-name=com.example.H2SQLExceptionOverride
public class H2SQLExceptionOverride implements SQLExceptionOverride {
private static final Logger logger = LoggerFactory.getLogger(H2SQLExceptionOverride.class);
public static final int LOCK_TIMOUT_ERROR_CODE = 50200;
#java.lang.Override
public Override adjudicate(SQLException sqlException) {
if (sqlException.getErrorCode() == LOCK_TIMOUT_ERROR_CODE) {
logger.debug("Diverting from default hikari impl and continuing transaction with errorCode: "
+ sqlException.getErrorCode() + " and sqlState: " + sqlException.getSQLState());
return Override.DO_NOT_EVICT;
}
return Override.CONTINUE_EVICT;
}
}
I am trying to put some data into Quartz job data map and access them in the class which implements the Job class. But it gives me the Null Pointer exception. When the application is run without the code which access the Job Data Map, it runs fine.
I use a Cron trigger to execute a scheduled job. In this example case, I configured it to run in each 20 seconds.
#Bean
public Trigger simpleJobTrigger(#Qualifier("simpleJobDetail") JobDetail jobDetail) {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setStartDelay(0L);
factoryBean.setName("test-trigger");
factoryBean.setStartTime(LocalDateTime.now().toDate());
factoryBean.setCronExpression("0/20 * * * * ?");
factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
try {
factoryBean.afterPropertiesSet();
} catch (ParseException e) {
e.printStackTrace();
}
return factoryBean.getObject();
}
Following is my simpleJobDetail bean.
#Bean
public JobDetailFactoryBean simpleJobDetail() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(Executor.class);
factoryBean.setDurability(true);
factoryBean.setName("test-job");
factoryBean.getJobDataMap().put("caller", "James");
return factoryBean;
}
This is my execute method.
public class Executor implements Job {
#Autowired
ScheduledTaskService scheduledTaskService;
#Override
public void execute(JobExecutionContext jobExecutionContext) {
JobDataMap jobDataMap = null;
try {
jobDataMap = jobExecutionContext.getTrigger().getJobDataMap();
String caller = jobDataMap.get("caller").toString();
System.out.println("This is called by the user "+caller);
} catch (Exception e) {
//e.printStackTrace();
System.out.println("UNABLE TO ACCESS THE JOB DATA MAP "+e);
}
scheduledTaskService.doThePayment();
}
}
When I run the application, it prints the log given in the catch clause.
UNABLE TO ACCESS THE JOB DATA MAP java.lang.NullPointerException
Why execute method fails to access the JobDataMap ? Is there any configuration or a property I should set? What is the reason for job map to be not available at this point?
How can I get this resolved?
Found the issue in my code.
When accessing the JobDataMap, I have accessed it in following way.
jobDataMap = jobExecutionContext.getTrigger().getJobDataMap();
instead, in my case, I should access it from JobDetails, not from the trigger.
jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
We are using DisallowConcurrentExecutionAttribute annotation inside the Java class to prevent concurrent execution of multiple instances, however, looks like Quartz has triggered twice the same instance concurrently. Please address this issue and provide us more information and fix this issue if it is a bug.
#Override
#Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void execute(final JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.log(Log.DEBUG, "++++ Quartz JOB BatchJobDetector started");
try {
this.setJobExecutionContext(jobExecutionContext);
boolean triggerNextJob = true;
while (triggerNextJob) {
TriggeredBatchProcessDTO triggeredBatchProcessDTO = getNextJob(jobExecutionContext, 0);
if (triggeredBatchProcessDTO != null) {
triggerJobImmediatly(triggeredBatchProcessDTO.getId(), jobExecutionContext);
triggeredBatchProcessDTO.setState(StatusType.RUNNING);
triggeredBatchProcessDTO.setProcessDtTm(triggeredBatchProcessDTO.getProcessDtTm());//CRGRO022
updateTriggeredBatchProcessDTO(triggeredBatchProcessDTO);
} else {
triggerNextJob = false;
}
}
} catch (final UnexpectedRuntimeException e) {
logger.log(Log.ERROR, "Error during execution of TriggeredBatchProcessDetectorJob: " + e.getMessage(), e);
throw e;
} catch (final Throwable t) {
throw new UnexpectedRuntimeException(CoreExceptionId.RUN_0001_UNEXPECTED_EXCEPTION,
new Object[] { "TriggeredBatchProcessDetectorJob error" }, t);
}
logger.log(Log.DEBUG, "++++ Quartz JOB BatchDetector finished");
}
You need to set up quartz correctly through properties to run it into the cluster mode and I'm not sure but imho you should also use #PersistJobDataAfterExecution annotation. I was using clustered quartz without any problems also with depracated job implementations
StatefulJob. You need to show us your config - here is sample - and give quartz lib versions
When I try to save a new entity to the database, I have the following error:
org.hibernate.AssertionFailure: null id in xxx.nameBean entry (don't flush the Session after an exception occurs)
produced at the code
session.save(nameBean)
but, "magically" it only appears at Production Server. When I try to reproduce the error at localhost, with the same code and data (using copy of the DB of Production Server, via bak file) it works ok.
What can it be?
EDIT: Adding the code that probably cause the error. The objective of that code is save the bean and update the otherBean in the same transaction, so if something wrong ocurrs make the rollback.
public class BeanDao extends ManagedSession {
public Integer save(Bean bean) {
Session session = null;
try {
session = createNewSessionAndTransaction();
Integer idValoracio = (Integer) session.save(bean);
doOtherAction(bean);
commitTransaction(session);
return idBean;
} catch (RuntimeException re) {
log.error("get failed", re);
if (session != null) {
rollbackTransaction(session);
}
throw re;
}
}
private void doOtherAction(Bean bean) {
Integer idOtherBean = bean.getIdOtherBean();
OtherBeanDao otherBeanDao = new OtherBeanDao();
OtherBean otherBean = otherBeanDao.findById(idOtherBean);
.
.
.
otherBeanDao.attachDirty(otherBean)
}
}
As the error message says, it's probably caused by attempt to use a session after it thrown an exception. Make sure your code doesn't swallow any Hibernate exceptions.