Wednesday, February 13, 2019

Create quartz scheduler as REST service and persist scheduler data to Database

Objective of this to create Scheduler as

1. REST service.
2. Persist scheduler data to a database

Here i use spring boot , quartz, mysql

Below is the link to sql script to create necessary table structure

https://cgenit.blogspot.com/2019/02/quartz-sql-script.html

Use Spring Initializer to create the project

https://start.spring.io/

Below is the POM file

https://cgenit.blogspot.com/2019/02/create-quartz-scheduler-as-rest-service_13.html

Few Annotations to be noted:
1. @EnableScheduling   - use in Springboot main class along with @SpringBootApplication
2. @PersistJobDataAfterExecution - used in Quartz job class eg: SyncJob


Below is the code for the Service

1. Controller class - with few selected service calls

package com.com.saap.scheduler.controller;

import java.util.List;

import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.com.saap.scheduler.model.SaapJob;
import com.com.saap.scheduler.model.SaapJob1;
import com.com.saap.scheduler.model.SaapJobKey;
import com.com.saap.scheduler.model.SaapTrigger;
import com.com.saap.scheduler.service.SchedulerService;

@RestController
@RequestMapping("sheduler")
public class ShedulerController {

@Autowired
SchedulerService schedulerService;

/**
* Returns list of schedule jobs.
*
* @return List list of jobs.
* @throws SchedulerException
*/
@GetMapping("/jobs")
public ResponseEntity> getJobList() throws SchedulerException {
List jobList = schedulerService.getJobList();

return new ResponseEntity>(jobList, HttpStatus.OK);
}

/**
* Filter list of jobs with give state.
*
* @param state state of the job.
* @return list of jobs.
* @throws SchedulerException
*/
@GetMapping("/jobs/{state}")
public ResponseEntity> getJobList(@PathVariable("state") String state) throws SchedulerException {
List jobList = schedulerService.getJobList(state);

return new ResponseEntity>(jobList, HttpStatus.OK);
}

/**
* Schedule a job. A new Trigger will be created and will be assign to same job.
*
* @param jobDetail {@link SaapJob} Information needs to create and schedule a
*                  job.
* @return
* @throws ClassNotFoundException
* @throws SchedulerException
*/
@PostMapping("job")
public ResponseEntity createSheduler(@RequestBody SaapJob1 jobDetail)
throws ClassNotFoundException, SchedulerException {

// pass the full object
schedulerService.sheduleJob(jobDetail.getJobClass(), jobDetail.getJobName(), jobDetail.getJobGroup(),
jobDetail.getTriggerName(), jobDetail.getTriggerGroup(), jobDetail.getCronExpression(),
jobDetail.getSourceURL(), jobDetail.getDestinationURL());

HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
* Delete a job with given {@link SaapJobKey}.
*
* @param jobkey combination of job name and corresponding group.
* @return
* @throws SchedulerException
*/
@PostMapping("job/del")
public ResponseEntity deleteJob(@RequestBody SaapJobKey jobkey) throws SchedulerException {
schedulerService.deleteJob(jobkey.getJobName(), jobkey.getJobGroup());
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
* Pause a job with given {@link SaapJobKey}.
*
* @param jobkey combination of job name and corresponding group.
* @return
* @throws SchedulerException
*/
@PostMapping("job/pause")
public ResponseEntity pauseJob(@RequestBody SaapJobKey jobkey) throws SchedulerException {
schedulerService.pauseJob(jobkey.getJobName(), jobkey.getJobGroup());
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
* Resume a job with given {@link SaapJobKey}.
*
* @param jobkey combination of job name and corresponding group.
* @return
* @throws SchedulerException
*/
@PostMapping("job/resume")
public ResponseEntity resumeJob(@RequestBody SaapJobKey jobkey) throws SchedulerException {
schedulerService.resumeJob(jobkey.getJobName(), jobkey.getJobGroup());
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
* Interrupt a job with given {@link SaapJobKey}.
*
* @param jobkey combination of job name and corresponding group.
* @return
* @throws SchedulerException
*/
@PostMapping("job/interrupt")
public ResponseEntity interruptJob(@RequestBody SaapJobKey jobkey) throws SchedulerException {
schedulerService.interruptJob(jobkey.getJobName(), jobkey.getJobGroup());
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
* Trigger a job with given {@link SaapJobKey}.
*
* @param jobkey combination of job name and corresponding group.
* @return
* @throws SchedulerException
*/
@PostMapping("job/trigger")
public ResponseEntity triggerJob(@RequestBody SaapJobKey jobkey) throws SchedulerException {
// TODO Auto-generated method stub
schedulerService.triggerJob(jobkey.getJobName(), jobkey.getJobGroup());
return null;
}

/**
* Change Cron expression of a trigger.
*
* @param triggerDetail Trigger details.
* @return
* @throws SchedulerException
*/
@PutMapping("job/trigger")
public ResponseEntity changeTrigger(@RequestBody SaapTrigger triggerDetail) throws SchedulerException {

schedulerService.changeTriggerForJob(triggerDetail.getTriggerKey().getName(),
triggerDetail.getTriggerKey().getGroup(), triggerDetail.getCronExpression());
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
*
* @return
* @throws SchedulerException
*/
@GetMapping("start")
public ResponseEntity start() throws SchedulerException {
schedulerService.start();
return new ResponseEntity(HttpStatus.OK);
}

/**
*
* @return
* @throws SchedulerException
*/
@GetMapping("shutdown")
public ResponseEntity shutdown() throws SchedulerException {
schedulerService.shutdown();
return new ResponseEntity(HttpStatus.OK);
}

}



2. Singleton class to create Scheduler object - not used as we use Spring bean

package com.com.saap.scheduler.core;

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

public class SaapScheduler {

private static volatile Scheduler scheduler = null;

// make this singleton
public Scheduler getScheduler() throws SchedulerException {
if (scheduler == null) {
scheduler = new StdSchedulerFactory().getScheduler();
}
return scheduler;
}

public void start(Scheduler scheduler) throws SchedulerException {
if (scheduler != null) {
scheduler.start();
}
}

public void shutDown(Scheduler scheduler) throws SchedulerException {
if (scheduler != null) {
scheduler.shutdown();
}
}
}


3. Sample Job classes

3. 1 HelloJob.java  -- simple job to print a line

package com.com.saap.scheduler.job;

import java.util.Date;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class HelloJob implements Job {

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// TODO Auto-generated method stub
System.out.println(new Date() + "Hello Quartz!");
}

}


3.2 SyncJob.java  --> do a Sync job. take data from one REST API call and POST to anther REST API

Note : @PersistJobDataAfterExecution

package com.com.saap.scheduler.job;

import java.util.Date;
import java.util.logging.Logger;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

@PersistJobDataAfterExecution
public class SyncJob implements Job {

private static Logger LOG = Logger.getLogger(SyncJob.class.getName());

public static final String SOURCE_URL = "sourceURL";
public static final String DESTINATION_URL = "destinationURL";

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// TODO Auto-generated method stub
System.out.println(new Date() + "SyncJob Quartz!");
JobKey jobKey = context.getJobDetail().getKey();

// Grab and print passed parameters
JobDataMap data = context.getJobDetail().getJobDataMap();
String sourceURL = data.getString(SOURCE_URL);
String destinationURL = data.getString(DESTINATION_URL);

LOG.info("************  stat start SyncJob ***************");
LOG.info("jobKey.getName() =" + jobKey.getName());
LOG.info("sourceURL =" + sourceURL);
LOG.info("destinationURL=" + destinationURL);
LOG.info("************  stat end SyncJob ***************");

/**
* Implementing Job Here
*
*/

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
RestTemplate restTemplate = new RestTemplate();
String url = data.getString(SOURCE_URL);
HttpEntity requestEntity = new HttpEntity(headers);

try {
ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity,
SyncJobResponse.class, 1);
LOG.info(responseEntity.toString());
} catch (Exception ex) {
System.out.println(ex.getLocalizedMessage());
}
}
}


4. Model classes use to transfer data 
4.1 SaapJob.java

package com.com.saap.scheduler.model;

import java.io.Serializable;

/**
 * Combination of the job functionality and the Trigger details relates to job.
 * 
 *
 */
public class SaapJob implements Serializable {

/**
*/
private static final long serialVersionUID = 688729605535527366L;

/**
* Details of the job functionality..
*/
private SaapJobDetail jobDetail;
/**
* Details of the Trigger.
*/
private SaapTrigger trigger;

public SaapJob(SaapJobDetail jobDetail, SaapTrigger trigger) {
this.jobDetail = jobDetail;
this.trigger = trigger;
}

public SaapJob() {

}

public SaapJobDetail getJobDetail() {
return jobDetail;
}

public void setJobDetail(SaapJobDetail jobDetail) {
this.jobDetail = jobDetail;
}

public SaapTrigger getTrigger() {
return trigger;
}

public void setTrigger(SaapTrigger trigger) {
this.trigger = trigger;
}

}


4.2 SaapJob1.java  - old class with all data

package com.com.saap.scheduler.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class SaapJob1 implements Serializable {

/**
*/
private static final long serialVersionUID = 2734719600011908418L;
private String jobClass;
private String jobName;
private String jobGroup;
private String triggerName;
private String triggerGroup;
private String cronExpression;
private String triggerState;
Date nextFireTime;

///
private String sourceURL;
private String destinationURL;
List jobDataList = new ArrayList();

public SaapJob1() {

}

public SaapJob1(String jobClass, String jobName, String jobGroup, String triggerName, String triggerGroup,
String cronExpression, Date nextFireTime, String triggerState) {
this.jobClass = jobClass;
this.jobName = jobName;
this.jobGroup = jobGroup;
this.triggerName = triggerName;
this.triggerGroup = triggerGroup;
this.cronExpression = cronExpression;
this.nextFireTime = nextFireTime != null ? (Date) nextFireTime.clone() : null;
this.triggerState = triggerState;
}

public String getJobClass() {
return jobClass;
}

public void setJobClass(String jobClass) {
this.jobClass = jobClass;
}

public String getJobName() {
return jobName;
}

public void setJobName(String jobName) {
this.jobName = jobName;
}

public String getJobGroup() {
return jobGroup;
}

public void setJobGroup(String jobGroup) {
this.jobGroup = jobGroup;
}

public String getTriggerName() {
return triggerName;
}

public void setTriggerName(String triggerName) {
this.triggerName = triggerName;
}

public String getTriggerGroup() {
return triggerGroup;
}

public void setTriggerGroup(String triggerGroup) {
this.triggerGroup = triggerGroup;
}

public String getCronExpression() {
return cronExpression;
}

public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}

public String getTriggerState() {
return triggerState;
}

public void setTriggerState(String triggerState) {
this.triggerState = triggerState;
}

public Date getNextFireTime() {
return nextFireTime != null ? (Date) nextFireTime.clone() : null;
}

public void setNextFireTime(Date nextFireTime) {
this.nextFireTime = nextFireTime != null ? (Date) nextFireTime.clone() : null;
}

public String getSourceURL() {
return sourceURL;
}

public void setSourceURL(String sourceURL) {
this.sourceURL = sourceURL;
}

public String getDestinationURL() {
return destinationURL;
}

public void setDestinationURL(String destinationURL) {
this.destinationURL = destinationURL;
}

public List getJobDataList() {
return jobDataList;
}

public void setJobDataList(List jobDataList) {
this.jobDataList = jobDataList;
}

}

4.3 SaapJobData

package com.com.saap.scheduler.model;

import java.io.Serializable;


public class SaapJobData implements Serializable {

/**
*
*/
private static final long serialVersionUID = 6234077751506588604L;
private String key;
private String value;

public SaapJobData() {

}

SaapJobData(String key, String value) {
this.key = key;
this.value = value;
}

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

}


4.4 SaapJobDetail

package com.com.saap.scheduler.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Conveys the detail properties of a given SaapJob instance.
 * 
 *
 * SaapobDetail contains
 *
     *


  1. jobClass
  2.  *


  3. jobDescription
  4.  *


  5. jobKey
  6.  *


  7. jobDataList
  8.  *
     *
     * 
     * 
     *
     */
    public class SaapJobDetail implements Serializable {

    /**
    */
    private static final long serialVersionUID = 3059373659998965232L;

    /**
    * name of the job class which logic is encapsulated.
    */
    private String jobClass;
    /**
    * Abstract description of the job.
    */
    private String jobDescription;
    /**
    * Unique job name and job group combination to identify the job.
    */
    private SaapJobKey jobKey;
    /**
    * List of {@link SaapJobData} which use as meta data for the job.
    */
    List jobDataList = new ArrayList();

    public SaapJobDetail() {

    }

    public SaapJobDetail(String jobClass, String jobDescription, SaapJobKey jobKey, List jobDataList) {
    this.jobClass = jobClass;
    this.jobDescription = jobDescription;
    this.jobKey = jobKey;
    this.jobDataList = jobDataList;
    }

    public String getJobClass() {
    return jobClass;
    }

    public void setJobClass(String jobClass) {
    this.jobClass = jobClass;
    }

    public String getJobDescription() {
    return jobDescription;
    }

    public void setJobDescription(String jobDescription) {
    this.jobDescription = jobDescription;
    }

    public SaapJobKey getJobKey() {
    return jobKey;
    }

    public void setJobKey(SaapJobKey jobKey) {
    this.jobKey = jobKey;
    }

    public List getJobDataList() {
    return jobDataList;
    }

    public void setJobDataList(List jobDataList) {
    this.jobDataList = jobDataList;
    }

    }


    4.5  SaapJobKey

    package com.com.saap.scheduler.model;

    import java.io.Serializable;

    /**
     * Job is identified uniquely using jobkey comprise of name and group. The
     * combination is unique.
     * 
     * 
     *
     */
    public class SaapJobKey implements Serializable {

    /**
    */
    private static final long serialVersionUID = 5196501106111618671L;
    /**
    * Name for particular job.
    */
    private String jobName;
    /**
    * Group name for particular job.
    */
    private String jobGroup;

    public SaapJobKey() {

    }

    public SaapJobKey(String jobName, String jobGroup) {
    this.jobName = jobName;
    this.jobGroup = jobGroup;
    }

    public String getJobName() {
    return jobName;
    }

    public void setJobName(String jobName) {
    this.jobName = jobName;
    }

    public String getJobGroup() {
    return jobGroup;
    }

    public void setJobGroup(String jobGroup) {
    this.jobGroup = jobGroup;
    }

    }


    4.6 SaapTrigger

    package com.com.saap.scheduler.model;

    import java.io.Serializable;
    import java.util.Date;
    import java.util.TimeZone;

    import org.quartz.Scheduler;
    import org.quartz.Trigger;

    /**
     *
     * SaapTriggerss have a {@link SaapTriggerKey} associated with
     * them, which should uniquely identify them within a single
     * {@link Scheduler}.
     *
     * 
     *
     * SaapTriggers are the 'mechanism' by which SaapJobs
     * are scheduled. Many SaapTriggers can point to the same
     * SaapJob, but a single SaapTrigger can only point to
     * one SaapJob.
     *
     * 
     *
     * SaapTriggers can 'send' parameters/data to SaapJobs by placing
     * contents into the List on the
     * SaapTrigger.
     *
     * 
     * 
     *
     */
    public class SaapTrigger implements Serializable {

    /**
    */
    private static final long serialVersionUID = 4507610386211548655L;
    private SaapTriggerKey triggerKey;
    private String description;
    private String cronExpression;
    private int priority = Trigger.DEFAULT_PRIORITY;
    private String triggerState;
    private Date startTime = null;
    private Date endTime = null;
    private Date nextFireTime = null;
    private Date previousFireTime = null;
    private transient TimeZone timeZone = null;

    public SaapTrigger() {

    }

    public SaapTrigger(SaapTriggerKey triggerKey, String description, String cronExpression, int priority) {
    this.triggerKey = triggerKey;
    this.description = description;
    this.cronExpression = cronExpression;
    this.priority = priority;
    }

    public SaapTriggerKey getTriggerKey() {
    return triggerKey;
    }

    public void setTriggerKey(SaapTriggerKey triggerKey) {
    this.triggerKey = triggerKey;
    }

    public String getDescription() {
    return description;
    }

    public void setDescription(String description) {
    this.description = description;
    }

    public String getCronExpression() {
    return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
    this.cronExpression = cronExpression;
    }

    public int getPriority() {
    return priority;
    }

    public void setPriority(int priority) {
    this.priority = priority;
    }

    public String getTriggerState() {
    return triggerState;
    }

    public void setTriggerState(String triggerState) {
    this.triggerState = triggerState;
    }

    public Date getStartTime() {
    return startTime != null ? (Date) startTime.clone() : null;
    }

    public void setStartTime(Date startTime) {
    this.startTime = startTime != null ? (Date) startTime.clone() : null;
    }

    public Date getEndTime() {
    return endTime != null ? (Date) endTime.clone() : null;
    }

    public void setEndTime(Date endTime) {
    this.endTime = endTime != null ? (Date) endTime.clone() : null;
    }

    public Date getNextFireTime() {
    return nextFireTime != null ? (Date) nextFireTime.clone() : null;
    }

    public void setNextFireTime(Date nextFireTime) {
    this.nextFireTime = nextFireTime != null ? (Date) nextFireTime.clone() : null;
    }

    public Date getPreviousFireTime() {
    return previousFireTime != null ? (Date) previousFireTime.clone() : null;
    }

    public void setPreviousFireTime(Date previousFireTime) {
    this.previousFireTime = previousFireTime != null ? (Date) previousFireTime.clone() : null;
    }

    public TimeZone getTimeZone() {
    return timeZone;
    }

    public void setTimeZone(TimeZone timeZone) {
    this.timeZone = timeZone;
    }

    }


    4.7 SaapTriggerKey

    package com.com.saap.scheduler.model;

    import java.io.Serializable;

    /**
     * Uniquely identifies a {@link SaapTriggerKey}.
     * 
     *
     * Keys are composed of both a name and group, and the name must be unique
     * within the group. If only a name is specified then the default group name
     * will be used.
     *
     * 
     * 
     *
     */
    public class SaapTriggerKey implements Serializable {

    /**
    */
    private static final long serialVersionUID = 2271019390246303775L;
    /**
    * The default group for scheduling entities, with the value "DEFAULT".
    */
    public static final String DEFAULT_GROUP = "DEFAULT";

    /**
    * name of the Trigger.
    */
    private String name;
    /**
    * group of the Trigger.
    */
    private String group;

    public SaapTriggerKey() {

    }

    public SaapTriggerKey(String name) {
    this(name, null);
    }

    public SaapTriggerKey(String name, String group) {

    if (name == null)
    throw new IllegalArgumentException("Name cannot be null.");
    this.name = name;
    if (group != null)
    this.group = group;
    else
    this.group = DEFAULT_GROUP;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public String getGroup() {
    return group;
    }

    public void setGroup(String group) {
    this.group = group;
    }

    @Override
    public String toString() {
    return "SaapTriggerKey [name=" + name + ", group=" + group + "]";
    }

    @Override
    public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((group == null) ? 0 : group.hashCode());
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
    }

    @Override
    public boolean equals(Object obj) {
    if (this == obj)
    return true;
    if (obj == null)
    return false;
    if (getClass() != obj.getClass())
    return false;
    equalsTriggerKey(obj);
    return true;
    }

    private boolean equalsTriggerKey(Object obj) {
    SaapTriggerKey other = (SaapTriggerKey) obj;
    if (group == null) {
    if (other.group != null)
    return false;
    } else if (!group.equals(other.group))
    return false;
    if (name == null) {
    if (other.name != null)
    return false;
    } else if (!name.equals(other.name))
    return false;

    return false;
    }

    }


    4.8 SaapTriggerState

    package com.com.saap.scheduler.model;

    /**
     * Trigger states {@link SaapTriggerState} for the schedule job.State helps to
     * find the actual state of the job as trigger is associated with a job.
     * 
     *
     * five states of the trigger are as follows
     *
       *


    1. NONE
    2.  *


    3. NORMAL
    4.  *


    5. PAUSED
    6.  *


    7. COMPLETE
    8.  *


    9. ERROR
    10.  *


    11. BLOCKED
    12.  *
       *
       * 
       * 
       *
       */
      public enum SaapTriggerState {
      NONE, NORMAL, PAUSED, COMPLETE, ERROR, BLOCKED
      }

      5. Service classes

      5.1 JobService

      package com.com.saap.scheduler.service;

      import org.quartz.Job;
      import org.quartz.JobDetail;
      import org.quartz.JobKey;

      public interface JobService {

      JobKey createJobKey(final String name);

      JobKey createJobKey(final String name, final String group);

      JobDetail createJobDetail(Class jobClass, final JobKey jobKey);

      }


      5.2 JobServiceImpl

      package com.com.saap.scheduler.service;

      import org.quartz.Job;
      import org.quartz.JobBuilder;
      import org.quartz.JobDetail;
      import org.quartz.JobKey;
      import org.springframework.stereotype.Service;

      @Service
      public class JobServiceImpl implements JobService {
      @Override
      public JobKey createJobKey(String name) {
      // TODO Auto-generated method stub
      return new JobKey(name);
      }

      @Override
      public JobKey createJobKey(String name, String group) {
      // TODO Auto-generated method stub
      return new JobKey(name, group);
      }

      ////
      @Override
      public JobDetail createJobDetail(Class jobClass, JobKey jobKey) {
      // TODO Auto-generated method stub
      return JobBuilder.newJob(jobClass).withIdentity(jobKey).build();
      }
      }


      5.3 SchedulerService

      package com.com.saap.scheduler.service;

      import java.util.List;

      import org.quartz.SchedulerException;
      import org.springframework.stereotype.Service;

      import com.com.saap.scheduler.model.SaapJob;

      @Service
      public interface SchedulerService {

      /**
      * Start scheduler.
      * @throws SchedulerException
      */
      void start() throws SchedulerException;

      /**
      * Shutdown scheduler.
      * @throws SchedulerException
      */
      void shutdown() throws SchedulerException;

      //// jobs

      /**
      * Returns all the jobs in the scheduler.
      * @return
      * @throws SchedulerException
      */
      List getJobList() throws SchedulerException;

      /**
      * Filter and return the list of jobs in the scheduler.
      * @param triggerState (NONE, NORMAL, PAUSED, COMPLETE, ERROR, BLOCKED)
      * @return
      * @throws SchedulerException
      */
      List getJobList(String triggerState) throws SchedulerException;

      /**
      * Trigger a selected job.
      * @param jobName
      * @param jobGroup
      * @throws SchedulerException
      */
      void triggerJob(String jobName, String jobGroup) throws SchedulerException;

      /**
      * Create Job and Trigger. Schedule the Job with created Trigger.
      * @param JobClass
      * @param jobName
      * @param jobGroup
      * @param triggerName
      * @param triggerGroup
      * @param cronExpression
      * @throws SchedulerException
      * @throws ClassNotFoundException
      */
      public void sheduleJob(final String JobClass, final String jobName, String jobGroup, final String triggerName,
      final String triggerGroup, final String cronExpression, final String sourceURL, final String destURL)
      throws SchedulerException, ClassNotFoundException;

      /**
      * Change the trigger value: cron Expression for the given job.
      * @param triggerName
      * @param triggerGroup
      * @param newCronExp
      * @throws SchedulerException
      */
      void changeTriggerForJob(String triggerName, String triggerGroup, String newCronExp) throws SchedulerException;

      /**
      * Delete a job.
      * @param jobName
      * @param jobGroup
      * @throws SchedulerException
      */
      void deleteJob(final String jobName, String jobGroup) throws SchedulerException;

      /**
      * Pause schedule job.
      * @param jobName
      * @param jobGroup
      * @throws SchedulerException
      */
      void pauseJob(String jobName, String jobGroup) throws SchedulerException;

      /**
      * Resume a given job.
      * @param jobName
      * @param jobGroup
      * @throws SchedulerException
      */
      void resumeJob(String jobName, String jobGroup) throws SchedulerException;

      /**
      * Interrupt a given job.
      * @param jobName
      * @param jobGroup
      * @throws SchedulerException
      */
      void interruptJob(String jobName, String jobGroup) throws SchedulerException;

      /**
      * Get the Job details for given job name with corresponding group.
      * @param jobName
      * @param jobGroup
      * @return
      * @throws SchedulerException
      */
      SaapJob getJobDetail(String jobName, String jobGroup) throws SchedulerException;

      }


      5.4 SchedulerServiceImpl

      package com.com.saap.scheduler.service;

      import java.util.ArrayList;
      import java.util.List;
      import java.util.Map;
      import java.util.function.Predicate;
      import java.util.logging.Logger;
      import java.util.stream.Collectors;

      import org.quartz.CronScheduleBuilder;
      import org.quartz.CronTrigger;
      import org.quartz.Job;
      import org.quartz.JobDataMap;
      import org.quartz.JobDetail;
      import org.quartz.JobExecutionContext;
      import org.quartz.JobKey;
      import org.quartz.Scheduler;
      import org.quartz.SchedulerException;
      import org.quartz.Trigger;
      import org.quartz.TriggerBuilder;
      import org.quartz.TriggerKey;
      import org.quartz.impl.matchers.GroupMatcher;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;

      import com.com.saap.scheduler.model.SaapJob;
      import com.com.saap.scheduler.model.SaapJobData;
      import com.com.saap.scheduler.model.SaapJobDetail;
      import com.com.saap.scheduler.model.SaapJobKey;
      import com.com.saap.scheduler.model.SaapTrigger;
      import com.com.saap.scheduler.model.SaapTriggerKey;

      @Service
      public class SchedulerServiceImpl implements SchedulerService {

      private static Logger LOG = Logger.getLogger(SchedulerServiceImpl.class.getName());

      @Autowired
      Scheduler scheduler;

      @Autowired
      TriggerServiceImpl triggerService;

      @Autowired
      JobServiceImpl jobService;

      @Override
      public void start() throws SchedulerException {
      scheduler.start();
      }

      @Override
      public void shutdown() throws SchedulerException {
      scheduler.shutdown();
      }

      public void addJob(final String JobClass, final String jobName, String jobGroup)
      throws SchedulerException, ClassNotFoundException {
      JobKey jobKey = jobService.createJobKey(jobName, jobGroup);
      JobDetail jobDetail = jobService.createJobDetail((Class) Class.forName(JobClass), jobKey);
      scheduler.addJob(jobDetail, true);
      }

      @Override
      public void sheduleJob(final String JobClass, final String jobName, String jobGroup, final String triggerName,
      final String triggerGroup, final String cronExpression, final String sourceURL, final String destURL)
      throws SchedulerException, ClassNotFoundException {

      JobKey jobKey = jobService.createJobKey(jobName, jobGroup);
      JobDetail jobDetail = jobService.createJobDetail((Class) Class.forName(JobClass), jobKey);
      jobDetail.getJobDataMap().put("sourceURL", sourceURL);
      jobDetail.getJobDataMap().put("destinationURL", destURL);
      TriggerKey triggerKey = triggerService.createTriggerKey(triggerName, triggerGroup);
      Trigger trigger = triggerService.createTrigger(triggerKey, cronExpression);
      scheduler.scheduleJob(jobDetail, trigger);
      }

      @Override
      public void deleteJob(final String jobName, String jobGroup) throws SchedulerException {
      JobKey jobKey = jobService.createJobKey(jobName, jobGroup);
      scheduler.deleteJob(jobKey);
      }

      @Override
      public List getJobList() throws SchedulerException {

      List saapJobList = new ArrayList();

      for (String groupName : scheduler.getJobGroupNames()) {
      for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {

      String jobName = jobKey.getName();
      String jobGroup = jobKey.getGroup();
      String jobDescription = scheduler.getJobDetail(jobKey).getDescription();
      String jobClass = scheduler.getJobDetail(jobKey).getJobClass().getName();

      List jobDataList = new ArrayList();
      JobDetail jobDetail = scheduler.getJobDetail(jobKey);

      if (jobDetail.getJobDataMap() != null && jobDetail.getJobDataMap().size() > 0) {
      JobDataMap jobDataMap = jobDetail.getJobDataMap();

      // check for null pointer
      for (Map.Entry entry : jobDataMap.entrySet()) {

      SaapJobData jobData = new SaapJobData();
      jobData.setKey(entry.getKey());
      jobData.setValue(entry.getValue().toString());
      jobDataList.add(jobData);
      }
      }

      List triggers = (List) scheduler.getTriggersOfJob(jobKey);
      Trigger trigger = triggers.get(0);
      CronTrigger cronTrigger = (CronTrigger) trigger;
      String cronExpression = cronTrigger.getCronExpression();

      // create saapJobDetail
      SaapJobDetail saapJobDetail = new SaapJobDetail();
      saapJobDetail.setJobClass(jobClass);
      saapJobDetail.setJobDataList(jobDataList);
      saapJobDetail.setJobDescription(jobDescription);
      saapJobDetail.setJobKey(new SaapJobKey(jobName, jobGroup));
      // create Trigger details
      SaapTrigger saapTrigger = new SaapTrigger();
      saapTrigger.setCronExpression(cronExpression);
      saapTrigger.setDescription(trigger.getDescription());
      saapTrigger.setEndTime(trigger.getEndTime());
      saapTrigger.setNextFireTime(trigger.getNextFireTime());
      saapTrigger.setPreviousFireTime(trigger.getPreviousFireTime());
      saapTrigger.setPriority(trigger.getPriority());
      saapTrigger.setStartTime(trigger.getStartTime());
      saapTrigger.setTriggerKey(new SaapTriggerKey(trigger.getKey().getName(), trigger.getKey().getGroup()));
      saapTrigger.setTriggerState(scheduler.getTriggerState(trigger.getKey()).toString());

      SaapJob saapJob = new SaapJob(saapJobDetail, saapTrigger);
      saapJobList.add(saapJob);
      }
      }

      return saapJobList;
      }

      @Override
      public List getJobList(String triggerState) throws SchedulerException {
      // SaapTriggerState.valueOf(triggerState)
      Predicate jobPricate = job -> job.getTrigger().getTriggerState().equals(triggerState);

      List jobs = getJobList().stream().parallel().filter(jobPricate).collect(Collectors.toList());

      return jobs;
      }

      @Override
      public SaapJob getJobDetail(String jobName, String jobGroup) throws SchedulerException {
      JobKey jobKey = new JobKey(jobName, jobGroup);
      JobDetail jobDetail = scheduler.getJobDetail(jobKey);

      // create saapJobDetail
      SaapJobDetail saapJobDetail = new SaapJobDetail();
      saapJobDetail.setJobClass(jobDetail.getClass().getName());

      List jobDataList = new ArrayList();
      if (jobDetail.getJobDataMap() != null && jobDetail.getJobDataMap().size() > 0) {
      JobDataMap jobDataMap = jobDetail.getJobDataMap();

      // check for null pointer
      for (Map.Entry entry : jobDataMap.entrySet()) {

      SaapJobData jobData = new SaapJobData();
      jobData.setKey(entry.getKey());
      jobData.setValue(entry.getValue().toString());
      jobDataList.add(jobData);
      }
      }
      saapJobDetail.setJobDataList(jobDataList);

      saapJobDetail.setJobDescription(jobDetail.getDescription());
      saapJobDetail.setJobKey(new SaapJobKey(jobName, jobGroup));
      // create Trigger details
      List triggers = (List) scheduler.getTriggersOfJob(jobKey);
      CronTrigger trigger = (CronTrigger) triggers.get(0);

      SaapTrigger saapTrigger = new SaapTrigger();
      saapTrigger.setCronExpression(trigger.getCronExpression());
      saapTrigger.setDescription(trigger.getDescription());
      saapTrigger.setEndTime(trigger.getEndTime());
      saapTrigger.setNextFireTime(trigger.getNextFireTime());
      saapTrigger.setPreviousFireTime(trigger.getPreviousFireTime());
      saapTrigger.setPriority(trigger.getPriority());
      saapTrigger.setStartTime(trigger.getStartTime());
      saapTrigger.setTriggerKey(new SaapTriggerKey(trigger.getKey().getName(), trigger.getKey().getGroup()));
      saapTrigger.setTriggerState(scheduler.getTriggerState(trigger.getKey()).toString());

      SaapJob saapJob = new SaapJob(saapJobDetail, saapTrigger);

      return saapJob;
      }

      public void getJobDetails(String jobGroupName) throws SchedulerException {
      GroupMatcher.jobGroupEquals(jobGroupName);
      List jobGroupList = scheduler.getJobGroupNames();
      LOG.info(jobGroupList.toString());
      }

      public void getCurrentlyRunningJobs() throws SchedulerException {
      List list = scheduler.getCurrentlyExecutingJobs();
      LOG.info(list.toString());
      }

      @Override
      public void triggerJob(String jobName, String jobGroup) throws SchedulerException {
      // create when group is null - for default group
      JobKey jobKey = new JobKey(jobName, jobGroup);
      scheduler.triggerJob(jobKey);
      }

      @Override
      public void pauseJob(String jobName, String jobGroup) throws SchedulerException {
      JobKey jobKey = new JobKey(jobName, jobGroup);
      scheduler.pauseJob(jobKey);
      }

      @Override
      public void resumeJob(String jobName, String jobGroup) throws SchedulerException {
      JobKey jobKey = new JobKey(jobName, jobGroup);
      scheduler.resumeJob(jobKey);
      }

      @Override
      public void interruptJob(String jobName, String jobGroup) throws SchedulerException {
      JobKey jobKey = new JobKey(jobName, jobGroup);
      scheduler.interrupt(jobKey);
      }

      @Override
      public void changeTriggerForJob(String triggerName, String triggerGroup, String newCronExp)
      throws SchedulerException {
      TriggerKey oldTriggerKey = new TriggerKey(triggerName, triggerGroup);
      Trigger newTrigger = TriggerBuilder.newTrigger().withIdentity(oldTriggerKey)
      .withSchedule(CronScheduleBuilder.cronSchedule(newCronExp)).build();
      scheduler.rescheduleJob(oldTriggerKey, newTrigger);
      }
      }


      5.5 TriggerService

      package com.com.saap.scheduler.service;

      import org.quartz.Trigger;
      import org.quartz.TriggerKey;

      public interface TriggerService {
      // trigger
      TriggerKey createTriggerKey(final String name);

      TriggerKey createTriggerKey(final String name, final String group);

      Trigger createTrigger(final TriggerKey triggerKey, final String cronExpression);
      }


      5.6 TriggerServiceImpl

      package com.com.saap.scheduler.service;

      import org.quartz.CronScheduleBuilder;
      import org.quartz.Trigger;
      import org.quartz.TriggerBuilder;
      import org.quartz.TriggerKey;
      import org.springframework.stereotype.Service;

      @Service
      public class TriggerServiceImpl implements TriggerService {
      @Override
      public TriggerKey createTriggerKey(String name) {
      // TODO Auto-generated method stub
      return new TriggerKey(name);
      }

      @Override
      public TriggerKey createTriggerKey(String name, String group) {
      // TODO Auto-generated method stub
      return new TriggerKey(name, group);
      }

      ////
      @Override
      public Trigger createTrigger(TriggerKey triggerKey, String cronExpression) {
      // TODO Auto-generated method stub triggerDescription triggerPriority
      return TriggerBuilder.newTrigger().withDescription(null).withIdentity(triggerKey)
      .withPriority(Trigger.DEFAULT_PRIORITY).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
      .build();
      }
      }


      6. SaapSchedulerApplication  - main class for SpringBoot application
      Note: @EnableScheduling

      package com.saap.scheduler;

      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.scheduling.annotation.EnableScheduling;

      @SpringBootApplication
      @EnableScheduling
      public class SaapSchedulerApplication {

      public static void main(String[] args) {
      SpringApplication.run(SaapSchedulerApplication.class, args);
      }
      }


      7. application.properties

      # Spring properties
      spring.application.name= sheduler-service

      #map error
      server.error.path: /error

      # HTTP Server
      server.port=8889

      # Discovery Server Access
      #  1. DEV ONLY: Reduce the lease renewal interval to speed up registration
      #  2. Define URL of registration server (defaultZone)
      eureka.client.serviceUrl.defaultZone= http://localhost:1111/eureka/
      eureka.instance.leaseRenewalIntervalInSeconds=5

      using.spring.schedulerFactory=true

      ## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
      spring.datasource.url = jdbc:mysql://localhost:3306/qzDS
      spring.datasource.username = root
      spring.datasource.password = 123

      ## Hibernate Properties
      # The SQL dialect makes Hibernate generate better SQL for the chosen database
      spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
      # Hibernate ddl auto (create, create-drop, validate, update)
      spring.jpa.hibernate.ddl-auto = update

      ## QuartzProperties
      spring.quartz.job-store-type=jdbc
      #spring.quartz.jdbc.schema=classpath:org/quartz/impl/jdbcjobstore/tables_mysql_innodb.sql
      spring.quartz.properties.org.quartz.threadPool.threadCount=5


      8. quartz.properties - not needed as we are using Springboot

      ##scheduler
      #quartz.scheduler.instanceName = MyScheduler
      #
      ## thread-pool
      #org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
      #org.quartz.threadPool.threadCount=2
      #org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
      #
      ## job-store
      ##org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
      #
      ## others
      #org.quartz.jobStore.misfireThreshold = 60000
      #
      ## Using JobStoreTX
      ### Be sure to run the appropriate script(under docs/dbTables) first to create database/tables
      #org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
      #
      ## Configuring JDBCJobStore with the Table Prefix
      #org.quartz.jobStore.tablePrefix = QRTZ_
      #
      ## Using DriverDelegate
      #org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
      #
      ## Using datasource
      #org.quartz.jobStore.dataSource = qzDS
      #
      ## Define the datasource to use
      ##org.quartz.dataSource.qzDS.driver = com.ibm.db2.jcc.DB2Driver
      ##org.quartz.dataSource.qzDS.URL = jdbc:db2://localhost:50000/QZ_SMPL
      ##org.quartz.dataSource.qzDS.user = wiki_rw
      ##org.quartz.dataSource.qzDS.password = db24local
      ##org.quartz.dataSource.qzDS.maxConnections = 30
      #org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
      #org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/qzDS
      #org.quartz.dataSource.qzDS.user = root
      #org.quartz.dataSource.qzDS.password = 123
      ##org.quartz.dataSource.qzDS.maxConnections = 30



      Create quartz scheduler as REST service and persist scheduler data to Database dependencies

      This is the POM file file




      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>

      <groupId>com.saap.scheduler</groupId>
      <artifactId>saap-scheduler</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>

      <name>saap-scheduler</name>
      <description>Demo project for Spring Boot</description>

      <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.1.0.RELEASE</version>
      <relativePath /> <!-- lookup parent from repository -->
      </parent>

      <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
      <spring-cloud.version>Greenwich.M1</spring-cloud.version>
      </properties>

      <dependencies>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-quartz</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
      </dependency>

      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      </dependency>
      <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
      </dependency>
      <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
      </dependency>

      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      </dependency>
      </dependencies>

      <dependencyManagement>
      <dependencies>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring-cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
      </dependency>
      </dependencies>
      </dependencyManagement>

      <build>
      <plugins>
      <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      </plugins>
      </build>

      <repositories>
      <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
      <enabled>false</enabled>
      </snapshots>
      </repository>
      </repositories>


      </project>

      Quartz sql script

      #
      # Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
      #
      # PLEASE consider using mysql with innodb tables to avoid locking issues
      #
      # In your Quartz properties file, you'll need to set
      # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
      #

      DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
      DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
      DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
      DROP TABLE IF EXISTS QRTZ_LOCKS;
      DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
      DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
      DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
      DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
      DROP TABLE IF EXISTS QRTZ_TRIGGERS;
      DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
      DROP TABLE IF EXISTS QRTZ_CALENDARS;


      CREATE TABLE QRTZ_JOB_DETAILS
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          JOB_NAME  VARCHAR(200) NOT NULL,
          JOB_GROUP VARCHAR(200) NOT NULL,
          DESCRIPTION VARCHAR(250) NULL,
          JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
          IS_DURABLE VARCHAR(1) NOT NULL,
          IS_NONCONCURRENT VARCHAR(1) NOT NULL,
          IS_UPDATE_DATA VARCHAR(1) NOT NULL,
          REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
          JOB_DATA BLOB NULL,
          PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
      );

      CREATE TABLE QRTZ_TRIGGERS
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          TRIGGER_NAME VARCHAR(200) NOT NULL,
          TRIGGER_GROUP VARCHAR(200) NOT NULL,
          JOB_NAME  VARCHAR(200) NOT NULL,
          JOB_GROUP VARCHAR(200) NOT NULL,
          DESCRIPTION VARCHAR(250) NULL,
          NEXT_FIRE_TIME BIGINT(13) NULL,
          PREV_FIRE_TIME BIGINT(13) NULL,
          PRIORITY INTEGER NULL,
          TRIGGER_STATE VARCHAR(16) NOT NULL,
          TRIGGER_TYPE VARCHAR(8) NOT NULL,
          START_TIME BIGINT(13) NOT NULL,
          END_TIME BIGINT(13) NULL,
          CALENDAR_NAME VARCHAR(200) NULL,
          MISFIRE_INSTR SMALLINT(2) NULL,
          JOB_DATA BLOB NULL,
          PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
          FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
              REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
      );

      CREATE TABLE QRTZ_SIMPLE_TRIGGERS
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          TRIGGER_NAME VARCHAR(200) NOT NULL,
          TRIGGER_GROUP VARCHAR(200) NOT NULL,
          REPEAT_COUNT BIGINT(7) NOT NULL,
          REPEAT_INTERVAL BIGINT(12) NOT NULL,
          TIMES_TRIGGERED BIGINT(10) NOT NULL,
          PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
          FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
              REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
      );

      CREATE TABLE QRTZ_CRON_TRIGGERS
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          TRIGGER_NAME VARCHAR(200) NOT NULL,
          TRIGGER_GROUP VARCHAR(200) NOT NULL,
          CRON_EXPRESSION VARCHAR(200) NOT NULL,
          TIME_ZONE_ID VARCHAR(80),
          PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
          FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
              REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
      );

      CREATE TABLE QRTZ_SIMPROP_TRIGGERS
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          TRIGGER_NAME VARCHAR(200) NOT NULL,
          TRIGGER_GROUP VARCHAR(200) NOT NULL,
          STR_PROP_1 VARCHAR(512) NULL,
          STR_PROP_2 VARCHAR(512) NULL,
          STR_PROP_3 VARCHAR(512) NULL,
          INT_PROP_1 INT NULL,
          INT_PROP_2 INT NULL,
          LONG_PROP_1 BIGINT NULL,
          LONG_PROP_2 BIGINT NULL,
          DEC_PROP_1 NUMERIC(13,4) NULL,
          DEC_PROP_2 NUMERIC(13,4) NULL,
          BOOL_PROP_1 VARCHAR(1) NULL,
          BOOL_PROP_2 VARCHAR(1) NULL,
          PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
          FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
          REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
      );

      CREATE TABLE QRTZ_BLOB_TRIGGERS
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          TRIGGER_NAME VARCHAR(200) NOT NULL,
          TRIGGER_GROUP VARCHAR(200) NOT NULL,
          BLOB_DATA BLOB NULL,
          PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
          FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
              REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
      );

      CREATE TABLE QRTZ_CALENDARS
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          CALENDAR_NAME  VARCHAR(200) NOT NULL,
          CALENDAR BLOB NOT NULL,
          PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
      );

      CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          TRIGGER_GROUP  VARCHAR(200) NOT NULL,
          PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
      );

      CREATE TABLE QRTZ_FIRED_TRIGGERS
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          ENTRY_ID VARCHAR(95) NOT NULL,
          TRIGGER_NAME VARCHAR(200) NOT NULL,
          TRIGGER_GROUP VARCHAR(200) NOT NULL,
          INSTANCE_NAME VARCHAR(200) NOT NULL,
          FIRED_TIME BIGINT(13) NOT NULL,
          SCHED_TIME BIGINT(13) NOT NULL,
          PRIORITY INTEGER NOT NULL,
          STATE VARCHAR(16) NOT NULL,
          JOB_NAME VARCHAR(200) NULL,
          JOB_GROUP VARCHAR(200) NULL,
          IS_NONCONCURRENT VARCHAR(1) NULL,
          REQUESTS_RECOVERY VARCHAR(1) NULL,
          PRIMARY KEY (SCHED_NAME,ENTRY_ID)
      );

      CREATE TABLE QRTZ_SCHEDULER_STATE
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          INSTANCE_NAME VARCHAR(200) NOT NULL,
          LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
          CHECKIN_INTERVAL BIGINT(13) NOT NULL,
          PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
      );

      CREATE TABLE QRTZ_LOCKS
        (
          SCHED_NAME VARCHAR(120) NOT NULL,
          LOCK_NAME  VARCHAR(40) NOT NULL,
          PRIMARY KEY (SCHED_NAME,LOCK_NAME)
      );


      commit;

      Wednesday, January 16, 2019

      Create Global Exception with Custom error message for Springboot REST Application

      I have seen many projects in SpringBoot has written with multiple Exception classes. This is a huge mistake, hence thus this leads to

      • accumulation of Exception classes 
      • and hence methods are being getting longer with so many exceptions at the end.
      Another issue is some people has written error code inside the error message. This is mostly due to not understanding how to make custom error object.

      Below will sample code explaining adding a global exception and a custom error object with error message, error code  etc.

      What we create in this example
      1. Custom error object         = ApiError.java
      2. Custom exception class    = CustomException
      3. Handler class to handle the global exception = GlobalExceptionHandler

      Important:
      I'm using "Lombok" , so all getter, setter, builder code is being generated by Lombok.

      ***************************************************************************

      import java.util.Date;
      import java.util.List;

      import org.springframework.http.HttpStatus;

      import lombok.Builder;
      import lombok.Data;

      @Data
      @Builder

      public class ApiError{


      private HttpStatus status;
      private String code;
      private String message;
      private List errors;
      private String path;
      private Date timeStamp;
      private String source;
      }


      -------------------------------------------------------------------------------------

      public class CustomException extends Exception {
      /**
      *
      */
      private static final long serialVersionUID = 1581305064814093777L;

      /**
      * error code for the {@link CustomException}
      */
      private String code;

      public CustomException() {
      super();
      }

      public CustomException(final String message) {
      super(message);
      }

      public CustomException(final String errorCode, final String message) {
      super(message);
      this.code = errorCode;
      }

      public CustomException(final String message, final Throwable cause) {
      super(message, cause);
      }

      public CustomException(final String code, final String message, final Throwable cause) {
      super(message, cause);
      this.code = code;
      }

      public CustomException(final Throwable cause) {
      super(cause);
      }

      /**
      * Custom error code for the REST project.Code consist of 10 digits. Below is
      * the breakdown of the code description.
      *
      * @return error code for specific error.
      */
      public String getCode() {
      return code;
      }

      }


      --------------------------------------------------------------------------------------------------------

      import java.util.Arrays;
      import java.util.Date;

      import javax.servlet.http.HttpServletRequest;

      import org.springframework.http.HttpHeaders;
      import org.springframework.http.HttpStatus;
      import org.springframework.http.ResponseEntity;
      import org.springframework.web.bind.annotation.ControllerAdvice;
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.context.request.WebRequest;
      import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;



      /**
       * Common exception handler for REST.
       *
       * @author AMAL PRASAD
       * @version 1.0.0
       * @since 1.0.0
       */
      @ControllerAdvice
      public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

      private static final String REQUEST_PATH = "org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping";
      private static final String SOURCE = "REST";

      /**
      * Handle all the exceptions inside REST project.
      *
      * @param ex      {@link Exception} occurs inside REST project
      * @param request Incoming request to REST project.
      * @param servReq Incoming request to REST project
      * @return response of type {@link ResponseEntity}.
      */
      @ExceptionHandler({ Exception.class })
      public ResponseEntity handleAll(final Exception ex, final WebRequest request,
      final HttpServletRequest servReq) {
      logger.info(ex.getClass().getName());
      logger.error("error", ex);
      CustomException customException = null;
      if (ex instanceof CustomException) {
      customException = (CustomException) ex;
      } else {
      throw new IllegalStateException(HttpStatus.INTERNAL_SERVER_ERROR.name());
      }

      final ApiError apiError = ApiError.builder().status(HttpStatus.INTERNAL_SERVER_ERROR)
      .code(customException.getCode()).message(ex.getLocalizedMessage())
      .errors(Arrays.asList("error occurred")).path(servReq.getAttribute(REQUEST_PATH).toString())
      .source(SOURCE).timeStamp(new Date()).build();

      return new ResponseEntity(apiError, new HttpHeaders(), apiError.getStatus());
      }
      }




      Sunday, January 6, 2019

      Eclipse and STS build error importing Gradle project

      Issue


      FAILURE: Build failed with an exception.

      * What went wrong:
      A problem occurred configuring root project 'warehouse'.
      > Could not resolve all files for configuration ':classpath'.
         > Could not resolve org.springframework.boot:spring-boot-gradle-plugin:2.1.1.RELEASE.
           Required by:
               project :
            > Could not resolve org.springframework.boot:spring-boot-gradle-plugin:2.1.1.RELEASE.
               > Could not parse POM https://jcenter.bintray.com/org/springframework/boot/spring-boot-gradle-plugin/2.1.1.RELEASE/spring-boot-gradle-plugin-2.1.1.RELEASE.pom
                  > Could not resolve org.springframework.boot:spring-boot-tools:2.1.1.RELEASE.
                     > Could not resolve org.springframework.boot:spring-boot-tools:2.1.1.RELEASE.
                        > Could not parse POM https://jcenter.bintray.com/org/springframework/boot/spring-boot-tools/2.1.1.RELEASE/spring-boot-tools-2.1.1.RELEASE.pom
                           > Could not resolve org.springframework.boot:spring-boot-parent:2.1.1.RELEASE.
                              > Could not resolve org.springframework.boot:spring-boot-parent:2.1.1.RELEASE.
                                 > Could not parse POM https://jcenter.bintray.com/org/springframework/boot/spring-boot-parent/2.1.1.RELEASE/spring-boot-parent-2.1.1.RELEASE.pom
                                    > Could not resolve org.springframework.boot:spring-boot-dependencies:2.1.1.RELEASE.
                                       > Could not resolve org.springframework.boot:spring-boot-dependencies:2.1.1.RELEASE.
                                          > Could not parse POM https://jcenter.bintray.com/org/springframework/boot/spring-boot-dependencies/2.1.1.RELEASE/spring-boot-dependencies-2.1.1.RELEASE.pom
                                             > Could not resolve org.springframework.data:spring-data-releasetrain:Lovelace-SR3.
                                                > Could not resolve org.springframework.data:spring-data-releasetrain:Lovelace-SR3.
                                                   > Could not get resource 'https://jcenter.bintray.com/org/springframework/data/spring-data-releasetrain/Lovelace-SR3/spring-data-releasetrain-Lovelace-SR3.pom'.
                                                      > Could not HEAD 'https://jcenter.bintray.com/org/springframework/data/spring-data-releasetrain/Lovelace-SR3/spring-data-releasetrain-Lovelace-SR3.pom'.
                                                         > sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

      * Try:
      Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

      * Get more help at https://help.gradle.org

      CONFIGURE FAILED in 26s



      Solution

      Set Gradle installation directory to STS - Spring Tool Suite

      "Window" --> "Preferences"  ( Go to "Preferences" in "Window" menu )
      Select "Gradle" ( If cannot find , type 'gradle' in top left corner search tab
      Add Local Installation directory -> your gradle installation directory

      Friday, January 4, 2019

      Model Mapper Issues



      public class ObjectMapperUtils {
      
          private static ModelMapper modelMapper = new ModelMapper();
      
          /**
           * Model mapper property setting are specified in the following block.
           * Default property matching strategy is set to Strict see {@link MatchingStrategies}
           * Custom mappings are added using {@link ModelMapper#addMappings(PropertyMap)}
           */
          static {
              modelMapper = new ModelMapper();
              modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
          }
      
          /**
           * Hide from public usage.
           */
          private ObjectMapperUtils() {
          }
      
          /**
           * Note: outClass object must have default constructor with no arguments
      * * @param type of result object. * @param type of source object to map from. * @param entity entity that needs to be mapped. * @param outClass class of result object. * @return new object of outClass type. */
      public static <D, T> D map(final T entity, Class<D> outClass) { return modelMapper.map(entity, outClass); } /** * Note: outClass object must have default constructor with no arguments
      * * @param entityList list of entities that needs to be mapped * @param outCLass class of result list element * @param type of objects in result list * @param type of entity in entityList * @return list of mapped object with type. */
      public static <D, T> List<D> mapAll(final Collection<T> entityList, Class<D> outCLass) { return entityList.stream() .map(entity -> map(entity, outCLass)) .collect(Collectors.toList()); } /** * Maps {@code source} to {@code destination}. * * @param source object to map from * @param destination object to map to */ public static <S, D> D map(final S source, D destination) { modelMapper.map(source, destination); return destination; } }