The task of job is simple, just output the time in console. I need a button for excuting the logic immediately.
XxxService.class
#Override
#Transactional(rollbackFor = Exception.class)
public void run(Long[] jobIds) {
for(Long jobId : jobIds){
ScheduleUtils. run(scheduler, this.getById(jobId));
}
}
ScheduleUtils.class
public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
try {
JobDataMap dataMap = new JobDataMap();
dataMap.put(ScheduleJobEntity.JOB_PARAM_KEY, scheduleJob);
System.out.println(Thread.currentThread()+"need hello..."+new Date());
scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
} catch (SchedulerException e) {
throw new RRException("run fail", e);
}
}
MyJob:
#Data
#Component("HelloJob")
public class HelloJob extends CustomJob {
public String jobDescription = "演示Job";
#Override
public void run(String params) {
System.out.println(
Thread.currentThread() +"hello..."+new Date() + "params: " + params);
}
}
When I add #Transactional on the run(Long[] jobIds), the job does not run immediately and often run delay 15~20 seconds.
Thread[http-nio-8500-exec-1,5,main]need hello...Mon Jun 27 12:55:02 CST 2022
2022-06-27 12:55:24 INFO [bear] QuartzScheduler_BearScheduler-DESKTOP-521BOOH1656305645549_MisfireHandler org.springframework.scheduling.quartz.LocalDataSourceJobStore Handling 1 trigger(s) that missed their scheduled fire-time.
Thread[BearScheduler_Worker-3,5,main]hello...Mon Jun 27 12:55:24 CST 2022params: aaaa,bbbbbbb
When I delete #Transactional on the run(Long[] jobIds), the job will run immediately.
hread[http-nio-8500-exec-4,5,main]need hello...Mon Jun 27 12:59:40 CST 2022
Thread[BearScheduler_Worker-6,5,main]hello...Mon Jun 27 12:59:40 CST 2022params: aaaa,bbbbbbb
Why my job is handled by MisfireHandler? Why the job can not run immediately when #Transactional on the method? If you know the reason,please help me. Thanks
Related
I'm creating a delegate expression for a Camunda process the workflow works perfect but when it executes the delegate my services creates the objects and doesn't write them into the database.
this is the code:
#Component
public class CreateNewROAction implements JavaDelegate {
private final ROactionService rOactionService;
private final RiskService riskService;
private Logger logger = LoggerFactory.getLogger(CreateNewROAction.class);
public CreateNewROAction(ROactionService rOactionService, RiskService riskService) {
this.rOactionService = rOactionService;
this.riskService = riskService;
}
#Override
public void execute(DelegateExecution delegateExecution) throws Exception {
String processInstanceId = delegateExecution.getProcessInstanceId();
String riskId = delegateExecution.getVariable("riskId").toString();
Risk risk = riskService.findById(Long.parseLong(riskId));
ROaction rOaction = new ROaction();
rOaction.setProcessInstanceId(processInstanceId);
rOaction = rOactionService.save(rOaction);
logger.info("roAction object: " + rOaction.toString());
risk.setAction(rOaction);
risk = riskService.update(risk, risk.getId());
logger.info("risk object: " + risk.toString());
delegateExecution.setVariable("id", rOaction.getId());
}
}
the loggers indicate the result that I expected:
roAction object: ROaction(id=4, description=null, status=null, responsible=null, deadline=null, AchievementDate=null, effectivenessEvaluationDate=null, effectivenessEvaluation=null, processInstanceId=819, createdAt=Wed Jul 03 15:08:19 CET 2019, updatedAt=Wed Jul 03 15:08:19 CET 2019)
risk object: Risk(id=4, description=null, probability=0, impact=0, cause=null, consequence=null, createdAt=2019-07-03 15:07:04.975, updatedAt=2019-07-03 15:07:04.975, processes=[], action=ROaction(id=4, description=null, status=null, responsible=null, deadline=null, AchievementDate=null, effectivenessEvaluationDate=null, effectivenessEvaluation=null, processInstanceId=819, createdAt=Wed Jul 03 15:08:19 CET 2019, updatedAt=Wed Jul 03 15:08:19 CET 2019), processInstanceId=801)
but the database is empty.
Notes:
1. I had to change the entity's id to #GeneratedValue(strategy = GenerationType.SEQUENCE) because before that the service returns a null Id from save() function
2. the services work fine from controllers
The solution is to not call the service methods directly but to call them through private methods annotated by #Transactional. The reason is that Hibernate open connection with the database in the first call and then close it. The component has just one connexion to the Database so it can't access it after the first call. #Transactional open a new connection with the database for the method.
I am using java quartz schedular. I am able to schedule jobs perfectly, though what i want is wait for job to finish before runing the second round because the time it takes to run each job varies.
I used #DisallowConcurrentExecution, what it did is only make the job to run once and never again. From job listener shows that the job finished successfully once.
Job
=============================================================
#DisallowConcurrentExecution
public class SalesJob implements Job{
List<Transaction> unsentTransaction = new ArrayList<Transaction>();
List<Sale> sales = new ArrayList<Sale>();
public void execute(JobExecutionContext jec) throws JobExecutionException {
System.out.println("Sales Job. . .");
}
}
Job Listener:
public class SalesJobListener implements JobListener{
public static final String LISTENER_NAME = "dummyJobListenerName";
public String getName() {
return LISTENER_NAME;
}
public void jobToBeExecuted(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().toString();
System.out.println("jobToBeExecuted");
System.out.println("Job : " + jobName + " is going to start...");
}
public void jobExecutionVetoed(JobExecutionContext jec) {
System.out.println("jobExecutionVetoed");
}
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
System.out.println("jobWasExecuted");
String jobName = context.getJobDetail().getKey().toString();
System.out.println("Job : " + jobName + " is finished...");
System.out.println("=====================================");
System.out.println("==========" + new Date() + "===========");
if (!jobException.getMessage().equals("")) {
System.out.println(
"Exception thrown by: " + jobName + " Exception: " + jobException.getMessage());
}
}
}
This is the schedular
JobKey salesJobKey = new JobKey("salesJob", "group1");
JobDetail salesJob = JobBuilder.newJob(SalesJob.class)
.withIdentity(salesJobKey).build();
Trigger salesTrigger = TriggerBuilder
.newTrigger()
.withIdentity("salesTrigger", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getListenerManager().addJobListener(
new SalesJobListener(), KeyMatcher.keyEquals(salesJobKey)
);
scheduler.start();
scheduler.scheduleJob(salesJob, salesTrigger);
PROBLEM
it executed this time, Wed Nov 25 12:01:15 EAT 2015 and now is Wed Nov 25 12:32 2015, so basically i have waited > 30 mins. . . and there is no another job
That is saying the Scheduler is not working.
WHY?
you cannot execute a job at second 15 of a minute because the pattern: 0/5 * * * * ? makes scheduler to run ONLY at seconds 0 and 5 of each minute.
Using #DisallowConcurrentExecution will prevent execution a Job if another one of same type is already running.
SOLUTION:
The mistake is in the order of your code, you execute then scheduler (scheduler.start();) before tell that it must schedule a job (scheduler.scheduleJob(salesJob, salesTrigger);):
scheduler.start();
scheduler.scheduleJob(salesJob, salesTrigger);
Check this example and swap your lines:
scheduler.scheduleJob(salesJob, salesTrigger);
scheduler.start();
That's all...
Just Add the below null check
if(null != jobException) {
if (!jobException.getMessage().equals("")) {
logger.debug("Exception thrown by: " + jobName
+ " Exception: " + jobException.getMessage());
}
}
Your code will work
In our REST-Service we want to implement a job that checks something every 10 seconds. So we thought we could use Quartz to make a Job that cover this. But the problem is, that we need to inject a singleton, because it is used in the job and the job seems to be not in the context of our service, so the injected class is always null (NullPointerException).
So is there another possible solution to achieve such a job without using Quartz? Already tried to write our own JobFactory that connects the job with the BeanManager, but it didnt work at all.
This is the code for the job that is not working:
#Stateless
public class GCEStatusJob implements Job, Serializable{
private Logger log = LoggerFactory.getLogger(GCEStatusJob.class);
#Inject
SharedMemory sharedMemory;
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
GoogleComputeEngineFactory googleComputeEngineFactory = new GoogleComputeEngineFactory();
List<HeartbeatModel> heartbeatList = new ArrayList<>(sharedMemory.getAllHeartbeats());
List<GCE> gceList = googleComputeEngineFactory.listGCEs();
List<String> ipAddressList = gceList.stream().map(GCE::getIp).collect(Collectors.toList());
for(HeartbeatModel heartbeat : heartbeatList){
if(ipAddressList.contains(heartbeat.getIpAddress())){
long systemTime = System.currentTimeMillis();
if(systemTime-heartbeat.getSystemTime()>10000){
log.info("Compute Engine mit IP "+heartbeat.getIpAddress()+" antwortet nicht mehr. Wird neu gestartet!");
String name = gceList.stream().filter((i) -> i.getIp().equals(heartbeat.getIpAddress())).findFirst().get().getName();
googleComputeEngineFactory.resetGCE(name);
}
}
}
}
}
SharedMemory is always null.
I have used Scheduler context map to achive this. You can try this.
In REST API when we create a Scheduler we can use the Context map to pass the parameters to Job
#Path("job")
public class RESTApi {
private String _userID;
public String get_userID() {
return _userID;
}
public void set_userID(String _userID) {
this._userID = _userID;
}
#GET
#Path("/start/{userId}")
public void startJob(#PathParam("userId") String userID) {
_userID = userID;
try {
SimpleTrigger trigger = new SimpleTrigger();
trigger.setName("updateTrigger");
trigger.setStartTime(new Date(System.currentTimeMillis() + 1000));
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setRepeatInterval(1000);
JobDetail job = new JobDetail();
job.setName("updateJob");
job.setJobClass(GCEStatusJob.class);
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getContext().put("apiClass", this);
scheduler.start();
scheduler.scheduleJob(job, trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
}
JOB implementation
public class GCEStatusJob implements Job {
#Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
RESTApi apiClass;
try {
apiClass = ((RESTApi) arg0.getScheduler().getContext().get("apiClass"));
System.out.println("User name is" + apiClass.get_userID());
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
Correct me, if my understanding is wrong.
in the following code example the handler does not repect the call to setLevel.
Logger globalLogger = Logger.getLogger("");
Handler handler = new LogMailHandler();
handler.setLevel(Level.SEVERE);
globalLogger.addHandler(handler);
Logger local = Logger.getLogger(LogMailHandlerTest.class.getName());
local.severe("Test message severe...");
local.info("Test message info...");
LogMailHandler is defined as follows:
public class LogMailHandler extends Handler {
#Override
public void publish(LogRecord pRecord) {
System.out.println("Error registered..." + pRecord.getLevel().getName());
}
#Override
public void flush() {
}
#Override
public void close() throws SecurityException {
}
}
The output is:
Jan 19, 2015 5:20:33 PM com.idmedia.fts.exchange.helper.LogMailHandlerTest main
SEVERE: Test message severe...
Error registered...SEVERE
Jan 19, 2015 5:20:33 PM com.idmedia.fts.exchange.helper.LogMailHandlerTest main
INFO: Test message info...
Error registered...INFO
In my opinion the "Error registered...INFO" should not be there since the level of the handler was set to SEVERE.
Any suggestions?
You must test the LogRecord on publish method with boolean isLoggable(LogRecord) before printing on console. This method tests, among other things, if the LogRecord level is higher that the minimum (in your case SEVERE)
Facing terrible issue... .
I have two timers of Class java.util.Timer, scheduledReportTimer which sends daily emails and scheduleImmediateReportTimer which send emails on every 10 mins.
scheduledReportTimer is working perfectly fine.
scheduleImmediateReportTimer is runs every 10 mins but it seems its running twice in 10 mins or there are two thread gets created for scheduleImmediateReportTimer (i am not sure what exactly it is)
but it is calling the email sending method twice
i tested email sending logic for lots of conditions and which is perfect .
please help me.
public class ScheduleReportManager implements ServletContextListener{
private ServletContext application = null;
private Environment environment =null;
private Timer scheduledReportTimer=null;
private Timer scheduleImmediateReportTimer=null; //daily timer
private ConcurrentHashMap scheduledReportTimerTasks;
private ConcurrentHashMap scheduledImmediateReportTimerTasks; //daily timer hash map
ApplicationContext webContext=null;
public ScheduleReportManager() {
}
public void contextInitialized(ServletContextEvent servletContextEvent) {
try{
this.application= servletContextEvent.getServletContext();
webContext = (ApplicationContext) application.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//daily timer
if(application.getAttribute("scheduledReportTimer")==null)
{
application.setAttribute("scheduleReportManager",this);
this.scheduledReportTimer= new Timer(true);
setupSchedule(scheduledReportTimer, application);
application.setAttribute("scheduledReportTimer", scheduledReportTimer);
}
//timer for 10 mins
if(application.getAttribute("scheduleImmediateReportTimer")==null)
{
this.scheduleImmediateReportTimer= new Timer(true);
setupImmediateSchedule(scheduleImmediateReportTimer, application);
application.setAttribute("scheduleImmediateReportTimer", scheduleImmediateReportTimer);
}
Logger.global.log(Level.INFO, "ScheduledReportTimer: " + application.getServletContextName() + ": Setup completed");
} catch (Exception e)
{
Logger.global.log(Level.SEVERE, "Setup Report Timer Exception - " + e.toString());
}
}
//timer for 10 mins
public void setupImmediateSchedule(Timer scheduledImmediateReportTimer,ServletContext application ) {
scheduledImmediateReportTimerTasks= new ConcurrentHashMap();
ScheduleImmediateReportTimerTask immediateReportTimerTask = new ScheduleImmediateReportTimerTask(application,environment,this);
scheduledImmediateReportTimerTasks.put(environment.getCode(),immediateReportTimerTask);
scheduledImmediateReportTimer.schedule(immediateReportTimerTask,1000);
}
//timer for 10 mins
public void setTimerForImmediateReportExecution(ScheduleImmediateReportTimerTask immediateReportTimerTask){
Environment environment = immediateReportTimerTask.getEnvironment();
ServletContext application = immediateReportTimerTask.getApplication();
ScheduleImmediateReportTimerTask reportTimerTask= new ScheduleImmediateReportTimerTask(application,environment,this);
String environmentCode = environment.getCode();
synchronized (scheduledImmediateReportTimerTasks){
scheduledImmediateReportTimerTasks.put(environmentCode,reportTimerTask);
scheduleImmediateReportTimer.schedule(reportTimerTask,600000); // set timer for running every 10 mins
}
}
//daily timer
public void setTimerForNextExecution(ScheduledReportTimerTask timerTask, DateTime nextExecutionDateTime)
{
if(nextExecutionDateTime == null)
return;
Environment environment = timerTask.getEnvironment();
ServletContext application = timerTask.getApplication();
ScheduledReportTimerTask scheduledReportTimerTask = new ScheduledReportTimerTask(application,environment,this);
java.util.Date nextScheduleTime = nextExecutionDateTime.getDate();
String environmentCode = environment.getCode();
synchronized (scheduledReportTimerTasks){
scheduledReportTimerTasks.put(environmentCode,scheduledReportTimerTask);
Logger.global.log(Level.INFO, "ScheduledReportManager: next execution time is " + nextScheduleTime.toString());
scheduledReportTimer.schedule(scheduledReportTimerTask,nextScheduleTime);
}
}
//daily timer
public void setupSchedule(Timer scheduledReportTimer, ServletContext application) throws Exception{
this.environment = SpringBridgeUtil.retrieveEnvironment(application);
this.scheduledReportTimerTasks= new ConcurrentHashMap();
ScheduledReportTimerTask scheduledReportTimerTask = new ScheduledReportTimerTask(application,environment,this);
scheduledReportTimerTasks.put(environment.getCode(),scheduledReportTimerTask);
scheduledReportTimer.schedule(scheduledReportTimerTask,1000);
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
String contextName = application.getServletContextName();
if (scheduledReportTimer != null) {
scheduledReportTimer.cancel();
}
if(scheduleImmediateReportTimer !=null)
{
scheduleImmediateReportTimer.cancel();
}
Logger.global.log(Level.INFO, "scheduledReportTimer: " + contextName
+ ": Tasks cancelled due to context removal");
}
}
// sends email on every 10 mins
public class ScheduleImmediateReportTimerTask extends TimerTask {
#Override
public void run() {
try{
// send mail
sendNotifiationEmail();
}catch (Exception e) {
e.printStackTrace();
Logger.global.log(Level.INFO,"immediateScheduledReportTimerTask Exception " + e.toString());
}
}
}
i am using jdk 1.7 and spring mvc 3.5.0 .
Classes using java.util.Timer and import java.util.TimerTask;
Let me know if this is the correct way or is there any other way to get execute schedule task periodically .