1

I am not an expert with Java and Springboot but I am building a QuartzScheduler application with JDBCStore in Spring Boot, I am using @Configuration, @Repository, @Service annotations for different classes in different packages, Structure has been given below.

enter image description here

The MySQL DataSource is available in Controller classes with @Autowired to DataSource (which is as Private) but where as if i try with the same Autowiring in other service classes, it is not working and it is throwing Null Pointer exception.

While the application loading, the DataSource is not available but later on when i am checking with @PostConstruct by printing the DataSource, it is showing up the HikariPool-1 DataSource.

Able to get the DataSource name in the SimpleCronScheduler through the RestController Get Method, Where as if i @Autowire the SimpleCronScheduler in TriggerListenerService, I cannot get the object and cannot use its Private methods like DataSource etc.

spring.datasource.url = jdbc:mysql://localhost/quartzscheduler?useSSL=false
spring.datasource.username = root
spring.datasource.password = password
spring.datasource.driverClassName= com.mysql.jdbc.Driver
spring.quartz.jdbc.initialize-schema=never
spring.jpa.show-sql=true 
spring.datasource.tomcat.testOnBorrow=true 
spring.datasource.tomcat.validationQuery=SELECT 1
spring.datasource.jmx-enabled=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect = 
org.hibernate.dialect.MySQL5InnoDBDialect

DataSource is null in main() method. The scope the DataSource is available in config and Controller packages but where as in services package, it is not reachable and Null.

I am really confused what i am missing here, please guide me if there is any programming concept i need to learn here to get the scope of the DataSource is available across the application, With the spring boot, it should be availbale by default but i am facing issue in which i am unable to use it as it is throwing Null Pointer Exception even after Auto configured.

Looking forward to hear your guidance and suggestions...

------main method file-----

package com.bsq.quartzscheduler;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;

@ComponentScan
@EnableScheduling
@SpringBootApplication
@EnableAutoConfiguration 
public class QuartzSchedulerPocApplication {

    @Autowired
    static DataSource dataSource;

    public static void main(String[] args) {

        SpringApplication.run(QuartzSchedulerPocApplication.class, args);
        System.out.println(" ^^^^^^^^^^ DataSource Name: "+ dataSource);
    }
}

-----------------Controller file----------

package com.bsq.quartzscheduler.controllers;

import static org.quartz.CronScheduleBuilder.cronSchedule;


import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import static org.quartz.impl.matchers.EverythingMatcher.allJobs;
import static org.quartz.impl.matchers.EverythingMatcher.allTriggers;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;

import com.bsq.quartzscheduler.services.JobsListenerService;
import com.bsq.quartzscheduler.services.SampleJob;
import com.bsq.quartzscheduler.services.SampleJob2;
import com.bsq.quartzscheduler.services.SampleJob3;
import com.bsq.quartzscheduler.services.TriggerListenerService;
import java.io.IOException;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;

@Controller
public class SimpleCronScheduler  {

     private Logger logger = LoggerFactory.getLogger(getClass());

     @Autowired
     private JobsListenerService jobsListenerService;

     @Autowired
     private TriggerListenerService triggerListener;

     @Autowired
     private Scheduler sc; 

     @Autowired
     private JdbcTemplate jdbcTemplate;

     @Autowired
     private DataSource dataSource;

     @PostConstruct
     public DataSource init() throws SchedulerException, IOException {
            //sc = this.initiate();
            logger.info("Hello from SimpleCronScheduler Controller... and DataSource is: "+ dataSource); //Outputs as HikariPool-1
            return dataSource;
     } 



     public void insertRecord() {
         System.out.println(">>>>>>>DataSource : " + dataSource + "JDBC TEmplate: "+ jdbcTemplate); 
         String sql = "insert into qrtz_logtable (jobName, instance, createTime) values('BSQJ1', 'ANCE1','TIME')";
         try {
             System.out.println(">>>>>>>TRYING TO EXECUTE in insertRecord : " );
             jdbcTemplate.update(sql);

         } catch (Exception e) {
             System.out.println(">>>>>>>exception in insertRecord : " + e.getLocalizedMessage());

         }
     }

     @Bean
     public Scheduler getScheduler() {
         if ( sc == null ) {
             try {
                sc =  StdSchedulerFactory.getDefaultScheduler();
                sc.getListenerManager().addTriggerListener(triggerListener, allTriggers());
                sc.getListenerManager().addJobListener(jobsListenerService, allJobs());
                logger.info("Scheduler Name in getScheduler "+ sc.getSchedulerName());
            } catch (SchedulerException e) {
                logger.info("Exception in getScheduler method "+ e.getLocalizedMessage());
            }
         }
         return sc;
     }

    @Bean
    public String testMethod()  {
        logger.info( "Test Method Started "+dataSource);
        return "TMS and DatSource Name--- "+dataSource; // Outputs as HikariPool-1 when i make a get call from RestController to this method.
     }

     public String startScheduler(Scheduler sc) {
         try {
            sc = getScheduler();

            sc.start();
            logger.info( "Scheduler name "+ sc.getSchedulerName()+" Started ");
            return "Scheduler name "+ sc.getSchedulerName()+" Started ";
         } catch (SchedulerException e) {
            System.out.println("Start Scheduler exception "+ e.getMessage());
            logger.info("EXCEPTION in Start Schduler "+ e.getMessage());
            return "Failed to start Scheduler name "+ e.getMessage();
         }
      }

     public String stopScheduler(){
         try {
             sc = getScheduler();
             logger.info("Stopping Scheduler: "+ sc.getSchedulerName());
             sc.standby();
             logger.info("Keeping the scheduler in Standby "+ sc.getSchedulerName() );
             return "Keeping the scheduler in Standby "+ sc.getSchedulerName();
         } catch (SchedulerException  e ) {
             System.out.println("EXCEPTINO IN STOP SCHEDULER: "+ e.getMessage());
             logger.info("EXCEPTION in stop Schduler "+ e.getMessage());
             return "failed to Stop the the scheduler  "+e.getMessage();
         }
     }

     //Using CronTrigger to schedule the jobs
     public String scheduleJobs(String jobName, int jobNum, String cronExpression, String groupName, String triggerName)  {
         System.out.println("Entered into Schedule Jobs");
         JobDetail job = null;
         CronTrigger trigger = null;
         try {
             sc = getScheduler();

             if(jobNum==1) {
                 job = newJob(SampleJob.class).withIdentity(jobName, groupName).build();
             } else if(jobNum ==2 ) {
                 job = newJob(SampleJob2.class).withIdentity(jobName, groupName).build();
             } else {
                 job = newJob(SampleJob3.class).withIdentity(jobName, groupName).build();
             }
            // job = newJob(jobClass).withIdentity(jobName, groupName).build();
             trigger = newTrigger().withIdentity(triggerName, groupName).withSchedule(cronSchedule(cronExpression)).build();
             sc.getListenerManager().addTriggerListener(triggerListener, allTriggers());
             sc.getListenerManager().addJobListener(jobsListenerService, allJobs());
             sc.scheduleJob(job, trigger);
             logger.info(job.getKey() + " has been scheduled to run at: "  + " and repeat based on expression: "+ trigger.getCronExpression());
             return "Job Added and scheduled for "+ jobName;
        } catch (SchedulerException e) {
            System.out.println("Failed to get Scheduler"+e.getMessage());
            logger.info("Failed to get Scheduler "+ e.getMessage());
            return "Failed to add JOb "+ e.getMessage();
        }
             }

     public String runJob(String group, String jobName)    {     
         try {
             sc = getScheduler();
             sc.triggerJob(new JobKey(jobName, group));
             logger.info("Running the Job rightaway  "+ group+"-"+jobName);
             return "Triggered the Job to run now "+  group+"-"+jobName;
         } catch (SchedulerException  e ) {
             System.out.println("EXCEPTINO IN RUN JOB : "+ e.getMessage());
             logger.info("EXCEPTION in RUN JOB "+ e.getMessage());
             return "Failed to run the job"+e.getMessage();
         }
     }

     public String stopJob(String group, String jobName)    {    
         try {
             sc = getScheduler();    
             sc.interrupt(new JobKey(jobName, group));
             logger.info("Stopping the Job rightaway  "+ group+"-"+jobName);
             return "Triggered the Job to run now "+  group+"-"+jobName;
         } catch (SchedulerException  e ) {
             System.out.println("EXCEPTINO IN INTERRUPTION OF JOB : "+ e.getMessage());
             logger.info("EXCEPTION INTERRUPTION OF JOB JOB "+ e.getMessage());
             return "Failed to STOP the job"+e.getMessage();
         }
     }

     public String deleteJob(String group, String name) {
         try {
             sc = getScheduler();
             sc.deleteJob(new JobKey(name, group));
             return "Job Deleted: "+  group+"-"+name;
         } catch (SchedulerException e) {
             return "Failed to Delete JOB: "+  group+"-"+name+ "Error is "+e.getMessage();
         }
     }

     public String deleteAllJobs() throws SchedulerException {
         sc = getScheduler();
         for (String groupName : sc.getJobGroupNames()) {
             for (JobKey jobKey : sc.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
                 sc.deleteJob(jobKey);
             }
         }
         return "All Jobs Deleted: ";
     }

}

-------------------Service file----------- where i am unable to access the dataSource.

package com.bsq.quartzscheduler.services;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.Trigger.CompletedExecutionInstruction;
import org.quartz.TriggerListener;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.bsq.quartzscheduler.controllers.SimpleCronScheduler;

@Service
public class TriggerListenerService implements TriggerListener {

    private final Log logger = LogFactory.getLog(getClass());
    DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    @Autowired
    private SimpleCronScheduler scs;

    @Autowired
    private DataSource dataSource;

    @Override
    public String getName() {
        return "Main Trigger(IP) Listener";
    }

    @Override
    public void triggerComplete(Trigger trigger, JobExecutionContext context,
            CompletedExecutionInstruction triggerInstructionCode) {
        // do something with the event
        Date date = new Date();

        logger.info("Trigger Listerner###: Method: triggerComplete | Time: "+ dateFormat.format(date) +" | Job Name: " + context.getJobDetail().getKey().getName());
    }

    @Override
    public void triggerFired(Trigger trigger, JobExecutionContext context) {
        // do something with the event
        Date date = new Date();

        String sql = "insert into qrtz_logtable (jobName, instance, createTime) values("+context.getJobDetail().getKey().getName()+", "+context.getJobInstance()+","+dateFormat.format(date)+")";


        System.out.println("DataSource in tListerner "+dataSource); //Outputs as NULL

        try{
            scs.insertRecord();
        } catch (Exception e) {
            System.out.println("ERROR MESSAGE OF SCS: "+e.getMessage()); //Outputs as NULL
        }

        logger.info("Trigger Listerner###: Method: triggerFired | Time: "+ dateFormat.format(date) +" | Job Name: " + context.getJobDetail().getKey().getName());

    }
    @Override
    public void triggerMisfired(Trigger trigger) {
        // do something with the event
        String jName;
        Date date = new Date();
        try {
            Scheduler sc = StdSchedulerFactory.getDefaultScheduler();
            sc.getContext();
            jName = sc.getJobDetail(trigger.getJobKey()).getKey().getName();

            logger.info("Trigger Listerner###: Method: triggerMisfired | Time: "+ dateFormat.format(date) +" | Job Name: " + jName);

        } catch (SchedulerException e) {
            System.out.println("Exception in triggerMisfired " +e.getMessage());
        }   

        System.out.println("Trigger Listener###: Method:  triggerMisfired | Time:"+ dateFormat.format(date) +" | Job Name: " + trigger.getKey());

    }
    @Override
    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
        // do something with the event
        boolean veto = false;
        Date date = new Date();
        logger.info("Trigger Listerner###: Method: vetoJobExecution | Time: "+ dateFormat.format(date) +" | Job Name: " + context.getJobDetail().getKey().getName());

        return veto;
    }

}
UM1979
  • 162
  • 3
  • 15
  • Can you post your main method snippet – Dhiren Feb 17 '19 at 20:50
  • @Dhiren, Please find my main method file in my post with additional details. – UM1979 Feb 18 '19 at 05:11
  • There is issue the way you are accessing `datasource`, go to any controller class add `@Autowired DataSource dataSource;` and then check. Since datasource is static, it will load it first before datasource bean has been created. Brush up some of the java and spring basics. – dkb Feb 18 '19 at 05:33
  • @dkb, As mentioned already in my post, Yes datasource is accessible in Controller classes but not in the service classes, that is the issue i am facing. Sure i will brush up my skills. – UM1979 Feb 18 '19 at 06:35
  • okay, can you update your code in post, its bit misleading, it should be service class instead of application class. I am able to call datasource from Service class. – dkb Feb 18 '19 at 06:52
  • @UM1979 you can't autowire static instance variable like that. Better post your controller snippet as well. when the class loader loads the static values, the Spring context is not yet necessarily loaded, So the class loader won't properly inject the static class variable in the bean, in this condition you always get datasource null. – Dhiren Feb 18 '19 at 06:57
  • @dkb, posted the service class (TriggerListenerService.java) in my post. – UM1979 Feb 18 '19 at 07:16
  • @Dhiren, posted the controller snippet (SimpleCronScheduler.java) in my post for your reference. – UM1979 Feb 18 '19 at 07:16
  • Here is one of the solution: https://stackoverflow.com/a/51982052/2987755, https://stackoverflow.com/a/15211030/2987755 – dkb Feb 18 '19 at 07:37
  • @dkb, Thank you for the references, i will make changes accordingly and come back if required. What i understood from my code is somewhat the Services(listeners) are not wired into the application, hence they are not able to access them. I will look into it and come back. – UM1979 Feb 18 '19 at 10:26

1 Answers1

0

I think this can happen when you declare the Datasource type wrongly.

Please check the Datasource class type.

the Datasource should be declared as a standard JDBC DataSource (i.e. javax.sql.DataSource).

import javax.sql.DataSource;
....
@Autowired 
DataSource dataSource;
YONGSOO KIM
  • 1,150
  • 1
  • 8
  • 11
  • I am using the same and correct data type only as it is the one suggested by the STS ide when i try to autowire the DataSource. – UM1979 Feb 18 '19 at 02:48